Skip to content

Commit

Permalink
fix(stleary#887): regressions, unit tests
Browse files Browse the repository at this point in the history
- JSONArray now evaluates EOF accordingly for empty Array inputs.
- JSONTokener fixed indentation
- externalized two JSONMLTest cases
  • Loading branch information
rikkarth committed Apr 28, 2024
1 parent cf00ef3 commit 1ae43bd
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 81 deletions.
8 changes: 6 additions & 2 deletions src/main/java/org/json/JSONArray.java
Expand Up @@ -136,9 +136,13 @@ private void parseTokener(JSONTokener x, JSONParserConfiguration jsonParserConfi
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));
}

Expand Down
115 changes: 56 additions & 59 deletions src/main/java/org/json/JSONTokener.java
Expand Up @@ -325,77 +325,66 @@ public char nextClean() throws JSONException {


/**
* 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 <code>"test'</code>).
* 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
* <code>"</code>&nbsp;<small>(double quote)</small> or
* <code>'</code>&nbsp;<small>(single quote)</small>.
* @param strictMode If true, this implementation will not accept unbalanced quotes (e.g will not accept <code>"test'</code>).
* @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();
}
Expand All @@ -404,7 +393,6 @@ public String nextString(char quote, boolean strictMode) throws JSONException {
}
}


/**
* Get the text up but not including the specified character or the
* end of line, whichever comes first.
Expand Down Expand Up @@ -528,15 +516,24 @@ private JSONArray getJsonArray() {
}
}

/**
* 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);
Expand Down
9 changes: 2 additions & 7 deletions src/test/java/org/json/junit/CDLTest.java
Expand Up @@ -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
Expand All @@ -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.
Expand Down
36 changes: 30 additions & 6 deletions src/test/java/org/json/junit/JSONMLTest.java
Expand Up @@ -6,6 +6,11 @@

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;

Expand Down Expand Up @@ -648,16 +653,10 @@ public void toJSONObjectToJSONArray() {
// 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
Expand All @@ -666,6 +665,31 @@ public void toJSONObjectToJSONArray() {
Util.compareActualVsExpectedJsonObjects(jsonObjectFromObject, jsonObjectFromArray);
}

@Test
public void givenXmlStr_testToJSONArray_shouldEqualExpectedArray() throws IOException {
try (Stream<String> jsonLines = Files.lines(
Paths.get("src/test/resources/JSONArrayExpectedTestCaseForToJsonArrayTest.json"));
Stream<String> 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
Expand Down
7 changes: 4 additions & 3 deletions src/test/java/org/json/junit/JSONParserConfigurationTest.java
Expand Up @@ -47,14 +47,15 @@ public void givenInvalidInputArrays_testStrictModeTrue_shouldThrowJsonException(
}

@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
Expand Down Expand Up @@ -215,7 +216,7 @@ public void givenNonCompliantQuotes_testStrictModeTrue_shouldThrowJsonExceptionW
() -> 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]",
Expand Down
6 changes: 2 additions & 4 deletions src/test/java/org/json/junit/JSONTokenerTest.java
Expand Up @@ -94,11 +94,9 @@ public void testValid() {
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);
Expand Down
@@ -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
]
]
]
]

0 comments on commit 1ae43bd

Please sign in to comment.