-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(#887): complete strictMode for JSONArray #888
Conversation
@rikkarth Sorry for taking so long to complete the review. Comments added. |
change(stleary#887): input validation
@rikkarth Please merge from master to pick up the latest changes: |
@rikkarth Looks like there is a regression when parsing array elements with non-string simple values. Please add this unit test:
This test passes when strict mode is false, but fails with this output when strict mode is true: Similar problems occur in strict mode when the array elements are true/false, and null. |
I changed how I validated numbers, boolean and string values using JSONObject.stringToValue existing logic, I've also added what I believe a few, more robust, test cases to prevent more regressions. I hope the implementation looks cleaner now. Sorry I didn't catch this earlier. Correction - regression when parsing array elements with non-string simple values. /**
* Parses unquoted text from the JSON input. This could be the values true, false, or null, or it can be a number.
* Non-standard forms are also accepted. Characters are accumulated until the end of the text or a formatting
* character is reached.
*
* @param c The starting character.
* @return The parsed object.
* @throws JSONException If the parsed string is empty.
*/
private Object parsedUnquotedText(char c, boolean strictMode) {
StringBuilder sb = new StringBuilder();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
if (!this.eof) {
this.back();
}
String string = sb.toString().trim();
if (string.isEmpty()) {
throw this.syntaxError("Missing value");
}
Object stringToValue = JSONObject.stringToValue(string);
return strictMode ? getValidNumberOrBooleanFromObject(stringToValue) : stringToValue;
}
private Object getValidNumberOrBooleanFromObject(Object value) {
if (value instanceof Number || value instanceof Boolean) {
return value;
}
throw new JSONException(String.format("Value is not surrounded by quotes: %s", value));
} |
null without quotes is a valid JSON value, just like true and false (see https://json.org). Strict mode should allow this value when it appears without quotes. Please add this test case for coverage:
|
Done as requested. Now it was simple as adding it to the this method: private Object getValidNumberBooleanOrNullFromObject(Object value) {
if (value instanceof Number || value instanceof Boolean || value.equals(JSONObject.NULL)) {
return value;
}
throw new JSONException(String.format("Value is not surrounded by quotes: %s", value));
} |
@rikkarth, This string fails in strict mode because there are chars after the end of an empty array: |
Yes you are absolutely right. I will come back to you with a more solid, robust solution and design. Thank you for your patience. |
Branch strict-mode-unit-tests contains all of your code from this branch. In the JSONParserConfiguration default constructor, I forced strictMode to be true, and then modified the broken unit tests or replaced @test with @ignore.
You can fix these issues on your current branch, or you can take a new fork off of the strict-mode-unit-tests branch. |
I will review all this points and will come back to you with new feedback. At first glance, this should be relatively easy to achieve/fix. Thank you for this analysis. |
- JSONArray now evaluates EOF accordingly for empty Array inputs. - JSONTokener fixed indentation - externalized two JSONMLTest cases
All of the points you've highlighted were fixed. There are a few cases (e.g I removed the TODO's, comments, etc that you provided in strict-mode-unit-tests as I fixed each issue. I left some of the "important" TODO's the one's you wrote "TBD later" or "will revert later" so you could easily navigate back to them later.
Fixed.
Fixed.
I merged them into this branch to keep a linear history of what was affected in this PR. |
@rikkarth Nice work. |
The test for this use-case passes in The tokener is trying to understand if the value is enclosed within an array or object. And then the tokener in strictMode sees this value is not a Number, null or boolean ... it's a string without quotes. The question is: Why should this test pass in strictMode? The only answer I could get was - it shouldn't. The return when checkValid("1 2", String.class); //strictMode false output 1 2 |
Could the label for this PR be updated? It's no longer pending redesign but pending review. Thank you. |
I think the label still applies. The goal of issue #887 was just to catch trailing chars at the end of the JSON doc. There might be a simpler solution that hasn't been found yet. For example, adding properties to the tokener does not seem like the right direction, or the way that JSONArray() was rewritten. |
Fair enough. Did you have time to read my previous comment? It is the last point I have to complete the points you've defined previously. I can make this last test pass, I'm just wondering why should this test pass in strict mode? Do you have an opinion on it? |
Do you mean the "1 2" validity test? Do you think the result difference is due to code changes to enforce quote-delimited strings, or to detecting trailing chars? In the former case, no additional changes are needed. In the latter case, it should be handled by the redesign. In either case, you don't need to make it work now. |
Yes, the error is exactly that. Quote-delimited strings, which is coherent with the design of strictMode. If you change the test and put quotes around the string, while strictMode is true, then it passes the test. Please check screenshots bellow. |
What problem does this code solve? Does the code still compile with Java6? Risks Changes to the API? Will this require a new release? Should the documentation be updated? Does it break the unit tests? Was any code refactored in this commit? Review status Starting 3-day comment window |
Cleaned up strictMode true flag as instructed. |
Description
If in strict mode, and having reached the end of the JSONArray construction life-cycle with no errors, we then perform an input validation.
In this validation we explicitly check if (1.) the last bracket was found and (2.) if more characters exist after that.
If the next character is not the end of the array (0), then it means we have more characters after the final closing square bracket, so we throw an error message informing the array is not compliant and we give back the complete input to the user for easy debugging.
To ensure this logic doesn't affect 2D arrays I also created a new test for it.
From the examples provided in the issue created: #887
Additional Note
Since the logic is fully encapsulated inside
validateInput()
which is only run if strictMode is true, it is guaranteed that previous implementations are not affected when strictMode is false and that this implementation is modular and can be maintained, improved or removed by simply removingvalidateInput()
.Test cases added