diff --git a/src/main/java/org/json/JSONArray.java b/src/main/java/org/json/JSONArray.java index e7d5f52..3823598 100644 --- a/src/main/java/org/json/JSONArray.java +++ b/src/main/java/org/json/JSONArray.java @@ -136,9 +136,13 @@ public class JSONArray implements Iterable { case ']': if (strictMode) { cursor = x.nextClean(); - boolean isNotEoF = !x.end(); + boolean isEoF = x.end(); - if (isNotEoF && x.getArrayLevel() == 0) { + if (isEoF) { + break; + } + + if (x.getArrayLevel() == 0) { throw x.syntaxError(getInvalidCharErrorMsg(cursor)); } diff --git a/src/main/java/org/json/JSONTokener.java b/src/main/java/org/json/JSONTokener.java index 7eb6bdd..63effc5 100644 --- a/src/main/java/org/json/JSONTokener.java +++ b/src/main/java/org/json/JSONTokener.java @@ -325,77 +325,66 @@ public class JSONTokener { /** - * Return the characters up to the next close quote character. - * Backslash processing is done. The formal JSON format does not - * allow strings in single quotes, but an implementation is allowed to - * accept them. - * If strictMode is true, this implementation will not accept unbalanced quotes (e.g will not accept "test'). + * Return the characters up to the next close quote character. Backslash processing is done. The formal JSON format + * does not allow strings in single quotes, but an implementation is allowed to accept them. + * * @param quote The quoting character, either * " (double quote) or * ' (single quote). - * @param strictMode If true, this implementation will not accept unbalanced quotes (e.g will not accept "test'). * @return A String. * @throws JSONException Unterminated string or unbalanced quotes if strictMode == true. */ - public String nextString(char quote, boolean strictMode) throws JSONException { + public String nextString(char quote) throws JSONException { char c; StringBuilder sb = new StringBuilder(); for (;;) { c = this.next(); switch (c) { - case 0: - case '\n': - case '\r': - throw this.syntaxError("Unterminated string. " + + case 0: + case '\n': + case '\r': + throw this.syntaxError("Unterminated string. " + "Character with int code " + (int) c + " is not allowed within a quoted string."); - case '\\': - c = this.next(); - switch (c) { - case 'b': - sb.append('\b'); - break; - case 't': - sb.append('\t'); - break; - case 'n': - sb.append('\n'); - break; - case 'f': - sb.append('\f'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - String next = this.next(4); - try { - sb.append((char)Integer.parseInt(next, 16)); - } catch (NumberFormatException e) { - throw this.syntaxError("Illegal escape. " + - "\\u must be followed by a 4 digit hexadecimal number. \\" + next + " is not valid.", e); - } - break; - case '"': - case '\'': case '\\': - case '/': - sb.append(c); + c = this.next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + String next = this.next(4); + try { + sb.append((char) Integer.parseInt(next, 16)); + } catch (NumberFormatException e) { + throw this.syntaxError("Illegal escape. " + + "\\u must be followed by a 4 digit hexadecimal number. \\" + next + + " is not valid.", + e); + } + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw this.syntaxError("Illegal escape. Escape sequence \\" + c + " is not valid."); + } break; default: - throw this.syntaxError("Illegal escape. Escape sequence \\" + c + " is not valid."); - } - break; - default: - if (strictMode && c == '\"' && quote != c) { - throw this.syntaxError(String.format( - "Field contains unbalanced quotes. Starts with %s but ends with double quote.", quote)); - } - - if (strictMode && c == '\'' && quote != c) { - throw this.syntaxError(String.format( - "Field contains unbalanced quotes. Starts with %s but ends with single quote.", quote)); - } - if (c == quote) { return sb.toString(); } @@ -404,7 +393,6 @@ public class JSONTokener { } } - /** * Get the text up but not including the specified character or the * end of line, whichever comes first. @@ -528,15 +516,24 @@ public class JSONTokener { } } + /** + * Get the next simple value from the JSON input. Simple values include strings (wrapped in single or double + * quotes), numbers, booleans, and null. This method is called when the next character is not '{' or '['. + * + * @param c The starting character. + * @param jsonParserConfiguration The configuration object containing parsing options. + * @return The parsed simple value. + * @throws JSONException If there is a syntax error or the value does not adhere to the configuration rules. + */ Object nextSimpleValue(char c, JSONParserConfiguration jsonParserConfiguration) { boolean strictMode = jsonParserConfiguration.isStrictMode(); - if(strictMode && c == '\''){ + if (strictMode && c == '\'') { throw this.syntaxError("Single quote wrap not allowed in strict mode"); } if (c == '"' || c == '\'') { - return this.nextString(c, strictMode); + return this.nextString(c); } return parsedUnquotedText(c, strictMode); diff --git a/src/test/java/org/json/junit/CDLTest.java b/src/test/java/org/json/junit/CDLTest.java index 44e4e3d..511218e 100644 --- a/src/test/java/org/json/junit/CDLTest.java +++ b/src/test/java/org/json/junit/CDLTest.java @@ -24,16 +24,12 @@ public class CDLTest { * String of lines where the column names are in the first row, * and all subsequent rows are values. All keys and values should be legal. */ - // TODO: This regression causes several unit tests to fail in strictMode. - // a single quote embedded in a valid st ring value should be allowed. - // This probably needs to be fixed in the strictMode parser. private static final String LINES = "Col 1, Col 2, \tCol 3, Col 4, Col 5, Col 6, Col 7\n" + "val1, val2, val3, val4, val5, val6, val7\n" + "1, 2, 3, 4\t, 5, 6, 7\n" + "true, false, true, true, false, false, false\n" + "0.23, 57.42, 5e27, -234.879, 2.34e5, 0.0, 9e-3\n" + - // "\"va\tl1\", \"v\bal2\", \"val3\", \"val\f4\", \"val5\", \"va'l6\", val7\n"; - "\"va\tl1\", \"v\bal2\", \"val3\", \"val\f4\", \"val5\", \"val6\", val7\n"; + "\"va\tl1\", \"v\bal2\", \"val3\", \"val\f4\", \"val5\", \"va'l6\", val7\n"; /** * CDL.toJSONArray() adds all values as strings, with no filtering or @@ -46,8 +42,7 @@ public class CDLTest { "{\"Col 1\":\"1\", \"Col 2\":\"2\", \"Col 3\":\"3\", \"Col 4\":\"4\", \"Col 5\":\"5\", \"Col 6\":\"6\", \"Col 7\":\"7\"}, " + "{\"Col 1\":\"true\", \"Col 2\":\"false\", \"Col 3\":\"true\", \"Col 4\":\"true\", \"Col 5\":\"false\", \"Col 6\":\"false\", \"Col 7\":\"false\"}, " + "{\"Col 1\":\"0.23\", \"Col 2\":\"57.42\", \"Col 3\":\"5e27\", \"Col 4\":\"-234.879\", \"Col 5\":\"2.34e5\", \"Col 6\":\"0.0\", \"Col 7\":\"9e-3\"}, " + - // "{\"Col 1\":\"va\tl1\", \"Col 2\":\"v\bal2\", \"Col 3\":\"val3\", \"Col 4\":\"val\f4\", \"Col 5\":\"val5\", \"Col 6\":\"va'l6\", \"Col 7\":\"val7\"}]"; - "{\"Col 1\":\"va\tl1\", \"Col 2\":\"v\bal2\", \"Col 3\":\"val3\", \"Col 4\":\"val\f4\", \"Col 5\":\"val5\", \"Col 6\":\"val6\", \"Col 7\":\"val7\"}]"; + "{\"Col 1\":\"va\tl1\", \"Col 2\":\"v\bal2\", \"Col 3\":\"val3\", \"Col 4\":\"val\f4\", \"Col 5\":\"val5\", \"Col 6\":\"va'l6\", \"Col 7\":\"val7\"}]"; /** * Attempts to create a JSONArray from a null string. diff --git a/src/test/java/org/json/junit/JSONMLTest.java b/src/test/java/org/json/junit/JSONMLTest.java index d3dd95d..d356840 100644 --- a/src/test/java/org/json/junit/JSONMLTest.java +++ b/src/test/java/org/json/junit/JSONMLTest.java @@ -6,6 +6,11 @@ Public Domain. import static org.junit.Assert.*; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.json.*; import org.junit.Test; @@ -648,16 +653,10 @@ public class JSONMLTest { // create a JSON array from the original string and make sure it // looks as expected JSONArray jsonArray = JSONML.toJSONArray(xmlStr); - // TODO: The next 2 test cases fail due to a strictMode regression. They should be isolated in separate - // test cases and fixed. -// JSONArray expectedJsonArray = new JSONArray(expectedJSONArrayStr); -// Util.compareActualVsExpectedJsonArrays(jsonArray,expectedJsonArray); // restore the XML, then make another JSONArray and make sure it // looks as expected String jsonArrayXmlToStr = JSONML.toString(jsonArray); -// JSONArray finalJsonArray = JSONML.toJSONArray(jsonArrayXmlToStr); -// Util.compareActualVsExpectedJsonArrays(finalJsonArray, expectedJsonArray); // lastly, confirm the restored JSONObject XML and JSONArray XML look // reasonably similar @@ -666,6 +665,31 @@ public class JSONMLTest { Util.compareActualVsExpectedJsonObjects(jsonObjectFromObject, jsonObjectFromArray); } + @Test + public void givenXmlStr_testToJSONArray_shouldEqualExpectedArray() throws IOException { + try (Stream jsonLines = Files.lines( + Paths.get("src/test/resources/JSONArrayExpectedTestCaseForToJsonArrayTest.json")); + Stream xmlLines = Files.lines(Paths.get("src/test/resources/XmlTestCaseTestToJsonArray.xml"))) { + + String xmlStr = xmlLines.collect(Collectors.joining()); + String expectedJSONArrayStr = jsonLines.collect(Collectors.joining()); + + JSONArray jsonArray = JSONML.toJSONArray(xmlStr); + JSONArray expectedJsonArray = new JSONArray(expectedJSONArrayStr); + + assertEquals(expectedJsonArray.toString(), jsonArray.toString()); + //TODO Util.compareActualVsExpectedJsonArrays can be replaced with above assertEquals(expectedJsonArray.toString(), jsonArray.toString()) + Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray); + + String jsonArrayXmlToStr = JSONML.toString(jsonArray); + + JSONArray finalJsonArray = JSONML.toJSONArray(jsonArrayXmlToStr); + + //TODO Util.compareActualVsExpectedJsonArrays can be replaced with assertEquals(expectedJsonArray.toString(), finalJsonArray.toString()) + Util.compareActualVsExpectedJsonArrays(finalJsonArray, expectedJsonArray); + } + } + /** * Convert an XML document which contains embedded comments into * a JSONArray. Use JSONML.toString() to turn it into a string, then diff --git a/src/test/java/org/json/junit/JSONParserConfigurationTest.java b/src/test/java/org/json/junit/JSONParserConfigurationTest.java index 2cea15f..427aad4 100644 --- a/src/test/java/org/json/junit/JSONParserConfigurationTest.java +++ b/src/test/java/org/json/junit/JSONParserConfigurationTest.java @@ -47,14 +47,15 @@ public class JSONParserConfigurationTest { } @Test - public void givenEmptyArray_testStrictModeTrue_shouldNotThrowJsonException(){ + public void givenEmptyArray_testStrictModeTrue_shouldNotThrowJsonException() { JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration() .withStrictMode(true); String testCase = "[]"; JSONArray jsonArray = new JSONArray(testCase, jsonParserConfiguration); - System.out.println(jsonArray); + + assertEquals(testCase, jsonArray.toString()); } @Test @@ -215,7 +216,7 @@ public class JSONParserConfigurationTest { () -> new JSONArray(testCaseFour, jsonParserConfiguration)); assertEquals( - "Field contains unbalanced quotes. Starts with \" but ends with single quote. at 6 [character 7 line 1]", + "Value 'test' is not surrounded by quotes at 13 [character 14 line 1]", jeOne.getMessage()); assertEquals( "Single quote wrap not allowed in strict mode at 2 [character 3 line 1]", diff --git a/src/test/java/org/json/junit/JSONTokenerTest.java b/src/test/java/org/json/junit/JSONTokenerTest.java index 29e2fcd..ba07323 100644 --- a/src/test/java/org/json/junit/JSONTokenerTest.java +++ b/src/test/java/org/json/junit/JSONTokenerTest.java @@ -94,11 +94,9 @@ public class JSONTokenerTest { checkValid(" {} ",JSONObject.class); checkValid("{\"a\":1}",JSONObject.class); checkValid(" {\"a\":1} ",JSONObject.class); - // TODO: strictMode regression, needs to be fixed. - // checkValid("[]",JSONArray.class); + checkValid("[]",JSONArray.class); checkValid(" [] ",JSONArray.class); - // TODO: strictMode regression, needs to be fixed - // checkValid("[1,2]",JSONArray.class); + checkValid("[1,2]",JSONArray.class); checkValid("\n\n[1,2]\n\n",JSONArray.class); // TODO: strictMode regression, needs to be fixed // checkValid("1 2", String.class); diff --git a/src/test/resources/JSONArrayExpectedTestCaseForToJsonArrayTest.json b/src/test/resources/JSONArrayExpectedTestCaseForToJsonArrayTest.json new file mode 100644 index 0000000..6ce0864 --- /dev/null +++ b/src/test/resources/JSONArrayExpectedTestCaseForToJsonArrayTest.json @@ -0,0 +1,91 @@ +[ + "addresses", + { + "xsi:noNamespaceSchemaLocation": "test.xsd", + "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance" + }, + [ + "address", + { + "addrType": "my address" + }, + [ + "name", + { + "nameType": "my name" + }, + "Joe Tester" + ], + [ + "street", + "Baker street 5" + ], + [ + "NothingHere", + { + "except": "an attribute" + } + ], + [ + "TrueValue", + true + ], + [ + "FalseValue", + false + ], + [ + "NullValue", + null + ], + [ + "PositiveValue", + 42 + ], + [ + "NegativeValue", + -23 + ], + [ + "DoubleValue", + -23.45 + ], + [ + "Nan", + "-23x.45" + ], + [ + "ArrayOfNum", + [ + "value", + 1 + ], + [ + "value", + 2 + ], + [ + "value", + [ + "subValue", + { + "svAttr": "svValue" + }, + "abc" + ] + ], + [ + "value", + 3 + ], + [ + "value", + 4.1 + ], + [ + "value", + 5.2 + ] + ] + ] +] diff --git a/src/test/resources/XmlTestCaseTestToJsonArray.xml b/src/test/resources/XmlTestCaseTestToJsonArray.xml new file mode 100644 index 0000000..dfcf9d9 --- /dev/null +++ b/src/test/resources/XmlTestCaseTestToJsonArray.xml @@ -0,0 +1,27 @@ + + + +
+ Joe Tester + + + true + false + null + 42 + -23 + -23.45 + -23x.45 + + 1 + 2 + + abc + + 3 + 4.1 + 5.2 + +
+
\ No newline at end of file