| package com.fasterxml.jackson.core.read; |
| |
| import com.fasterxml.jackson.core.BaseTest; |
| import com.fasterxml.jackson.core.JsonFactory; |
| import com.fasterxml.jackson.core.JsonParser; |
| import com.fasterxml.jackson.core.JsonParser.Feature; |
| import com.fasterxml.jackson.core.JsonToken; |
| import com.fasterxml.jackson.core.json.UTF8DataInputJsonParser; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| |
| @RunWith(Parameterized.class) |
| public class TrailingCommasTest extends BaseTest { |
| |
| private final JsonFactory factory; |
| private final HashSet<JsonParser.Feature> features; |
| private final int mode; |
| |
| public TrailingCommasTest(int mode, List<Feature> features) { |
| this.factory = new JsonFactory(); |
| this.features = new HashSet<JsonParser.Feature>(features); |
| |
| for (JsonParser.Feature feature : features) { |
| factory.enable(feature); |
| } |
| |
| this.mode = mode; |
| } |
| |
| @Parameterized.Parameters(name = "Mode {0}, Features {1}") |
| public static Collection<Object[]> getTestCases() { |
| ArrayList<Object[]> cases = new ArrayList<Object[]>(); |
| |
| for (int mode : ALL_MODES) { |
| cases.add(new Object[]{mode, Collections.emptyList()}); |
| cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES)}); |
| cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_TRAILING_COMMA)}); |
| cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES, Feature.ALLOW_TRAILING_COMMA)}); |
| } |
| |
| return cases; |
| } |
| |
| @Test |
| public void testArrayBasic() throws Exception { |
| String json = "[\"a\", \"b\"]"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("a", p.getText()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("b", p.getText()); |
| |
| assertEquals(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } |
| |
| @Test |
| public void testArrayInnerComma() throws Exception { |
| String json = "[\"a\",, \"b\"]"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("a", p.getText()); |
| |
| if (!features.contains(Feature.ALLOW_MISSING_VALUES)) { |
| assertUnexpected(p, ','); |
| return; |
| } |
| |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("b", p.getText()); |
| |
| assertEquals(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } |
| |
| @Test |
| public void testArrayLeadingComma() throws Exception { |
| String json = "[,\"a\", \"b\"]"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
| |
| if (!features.contains(Feature.ALLOW_MISSING_VALUES)) { |
| assertUnexpected(p, ','); |
| return; |
| } |
| |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("a", p.getText()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("b", p.getText()); |
| |
| assertEquals(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } |
| |
| @Test |
| public void testArrayTrailingComma() throws Exception { |
| String json = "[\"a\", \"b\",]"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("a", p.getText()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("b", p.getText()); |
| |
| // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
| if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { |
| assertToken(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } else { |
| assertUnexpected(p, ']'); |
| } |
| } |
| |
| @Test |
| public void testArrayTrailingCommas() throws Exception { |
| String json = "[\"a\", \"b\",,]"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("a", p.getText()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("b", p.getText()); |
| |
| // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
| if (features.contains(Feature.ALLOW_MISSING_VALUES) && |
| features.contains(Feature.ALLOW_TRAILING_COMMA)) { |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } else { |
| assertUnexpected(p, ','); |
| } |
| } |
| |
| @Test |
| public void testArrayTrailingCommasTriple() throws Exception { |
| String json = "[\"a\", \"b\",,,]"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("a", p.getText()); |
| |
| assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
| assertEquals("b", p.getText()); |
| |
| // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
| if (features.contains(Feature.ALLOW_MISSING_VALUES) && |
| features.contains(Feature.ALLOW_TRAILING_COMMA)) { |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
| assertToken(JsonToken.END_ARRAY, p.nextToken()); |
| assertEnd(p); |
| } else { |
| assertUnexpected(p, ','); |
| } |
| } |
| |
| @Test |
| public void testObjectBasic() throws Exception { |
| String json = "{\"a\": true, \"b\": false}"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_OBJECT, p.nextToken()); |
| |
| assertToken(JsonToken.FIELD_NAME, p.nextToken()); |
| assertEquals("a", p.getText()); |
| assertToken(JsonToken.VALUE_TRUE, p.nextToken()); |
| |
| assertToken(JsonToken.FIELD_NAME, p.nextToken()); |
| assertEquals("b", p.getText()); |
| assertToken(JsonToken.VALUE_FALSE, p.nextToken()); |
| |
| assertEquals(JsonToken.END_OBJECT, p.nextToken()); |
| assertEnd(p); |
| } |
| |
| @Test |
| public void testObjectInnerComma() throws Exception { |
| String json = "{\"a\": true,, \"b\": false}"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_OBJECT, p.nextToken()); |
| |
| assertToken(JsonToken.FIELD_NAME, p.nextToken()); |
| assertEquals("a", p.getText()); |
| assertToken(JsonToken.VALUE_TRUE, p.nextToken()); |
| |
| assertUnexpected(p, ','); |
| } |
| |
| @Test |
| public void testObjectLeadingComma() throws Exception { |
| String json = "{,\"a\": true, \"b\": false}"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_OBJECT, p.nextToken()); |
| |
| assertUnexpected(p, ','); |
| } |
| |
| @Test |
| public void testObjectTrailingComma() throws Exception { |
| String json = "{\"a\": true, \"b\": false,}"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_OBJECT, p.nextToken()); |
| |
| assertToken(JsonToken.FIELD_NAME, p.nextToken()); |
| assertEquals("a", p.getText()); |
| assertToken(JsonToken.VALUE_TRUE, p.nextToken()); |
| |
| assertToken(JsonToken.FIELD_NAME, p.nextToken()); |
| assertEquals("b", p.getText()); |
| assertToken(JsonToken.VALUE_FALSE, p.nextToken()); |
| |
| if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { |
| assertToken(JsonToken.END_OBJECT, p.nextToken()); |
| assertEnd(p); |
| } else { |
| assertUnexpected(p, '}'); |
| } |
| } |
| |
| @Test |
| public void testObjectTrailingCommas() throws Exception { |
| String json = "{\"a\": true, \"b\": false,,}"; |
| |
| JsonParser p = createParser(factory, mode, json); |
| |
| assertEquals(JsonToken.START_OBJECT, p.nextToken()); |
| |
| assertToken(JsonToken.FIELD_NAME, p.nextToken()); |
| assertEquals("a", p.getText()); |
| assertToken(JsonToken.VALUE_TRUE, p.nextToken()); |
| |
| assertToken(JsonToken.FIELD_NAME, p.nextToken()); |
| assertEquals("b", p.getText()); |
| assertToken(JsonToken.VALUE_FALSE, p.nextToken()); |
| |
| assertUnexpected(p, ','); |
| } |
| |
| private void assertEnd(JsonParser p) throws IOException { |
| // Issue #325 |
| if (!(p instanceof UTF8DataInputJsonParser)) { |
| JsonToken next = p.nextToken(); |
| assertNull("expected end of stream but found " + next, next); |
| } |
| } |
| |
| private void assertUnexpected(JsonParser p, char c) throws IOException { |
| try { |
| p.nextToken(); |
| fail("No exception thrown"); |
| } catch (Exception e) { |
| verifyException(e, String.format("Unexpected character ('%s' (code %d))", c, (int) c)); |
| } |
| } |
| } |