mirror of
https://github.com/stleary/JSON-java.git
synced 2025-08-03 03:15:32 -04:00
feat(#871-strictMode): enhanced and simplified strictMode logic
This commit is contained in:
parent
49de92224d
commit
372f5caac4
@ -225,14 +225,7 @@ public class JSONObject {
|
|||||||
case '}':
|
case '}':
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
String keyToValidate = getKeyToValidate(x, c, jsonParserConfiguration.isStrictMode());
|
key = x.nextSimpleValue(c, jsonParserConfiguration.isStrictMode()).toString();
|
||||||
boolean keyHasQuotes = keyToValidate.startsWith("\"") && keyToValidate.endsWith("\"");
|
|
||||||
|
|
||||||
if (jsonParserConfiguration.isStrictMode() && !keyHasQuotes) {
|
|
||||||
throw new JSONException("Key is not surrounded by quotes: " + keyToValidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
key = keyToValidate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The key is followed by ':'.
|
// The key is followed by ':'.
|
||||||
@ -285,11 +278,6 @@ public class JSONObject {
|
|||||||
}
|
}
|
||||||
return x.nextValue();
|
return x.nextValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getKeyToValidate(JSONTokener x, char c, boolean strictMode) {
|
|
||||||
return x.nextSimpleValue(c, strictMode).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a JSONObject from a Map.
|
* Construct a JSONObject from a Map.
|
||||||
*
|
*
|
||||||
|
@ -284,62 +284,73 @@ public class JSONTokener {
|
|||||||
* Backslash processing is done. The formal JSON format does not
|
* Backslash processing is done. The formal JSON format does not
|
||||||
* allow strings in single quotes, but an implementation is allowed to
|
* allow strings in single quotes, but an implementation is allowed to
|
||||||
* accept them.
|
* accept them.
|
||||||
|
* If strictMode is true, this implementation will not accept unbalanced quotes (e.g will not accept <code>"test'</code>)
|
||||||
* @param quote The quoting character, either
|
* @param quote The quoting character, either
|
||||||
* <code>"</code> <small>(double quote)</small> or
|
* <code>"</code> <small>(double quote)</small> or
|
||||||
* <code>'</code> <small>(single quote)</small>.
|
* <code>'</code> <small>(single quote)</small>.
|
||||||
* @return A String.
|
* @return A String.
|
||||||
* @throws JSONException Unterminated string.
|
* @throws JSONException Unterminated string or unbalanced quotes if strictMode == true.
|
||||||
*/
|
*/
|
||||||
public String nextString(char quote) throws JSONException {
|
public String nextString(char quote, boolean strictMode) throws JSONException {
|
||||||
char c;
|
char c;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = this.next();
|
c = this.next();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 0:
|
case 0:
|
||||||
case '\n':
|
case '\n':
|
||||||
case '\r':
|
case '\r':
|
||||||
throw this.syntaxError("Unterminated string");
|
throw this.syntaxError("Unterminated string");
|
||||||
case '\\':
|
case '\\':
|
||||||
c = this.next();
|
c = this.next();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'b':
|
case 'b':
|
||||||
sb.append('\b');
|
sb.append('\b');
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
sb.append('\t');
|
sb.append('\t');
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
sb.append('\f');
|
sb.append('\f');
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
sb.append('\r');
|
sb.append('\r');
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
try {
|
try {
|
||||||
sb.append((char)Integer.parseInt(this.next(4), 16));
|
sb.append((char) Integer.parseInt(this.next(4), 16));
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw this.syntaxError("Illegal escape.", e);
|
throw this.syntaxError("Illegal escape.", e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
case '\'':
|
||||||
|
case '\\':
|
||||||
|
case '/':
|
||||||
|
sb.append(c);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw this.syntaxError("Illegal escape.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '"':
|
|
||||||
case '\'':
|
|
||||||
case '\\':
|
|
||||||
case '/':
|
|
||||||
sb.append(c);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw this.syntaxError("Illegal escape.");
|
if (strictMode && c == '\"' && quote != c) {
|
||||||
}
|
throw this.syntaxError(String.format(
|
||||||
break;
|
"Field contains unbalanced quotes. Starts with %s but ends with double quote.", quote));
|
||||||
default:
|
}
|
||||||
if (c == quote) {
|
|
||||||
return sb.toString();
|
if (strictMode && c == '\'' && quote != c) {
|
||||||
}
|
throw this.syntaxError(String.format(
|
||||||
sb.append(c);
|
"Field contains unbalanced quotes. Starts with %s but ends with single quote.", quote));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == quote) {
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
sb.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -423,50 +434,10 @@ public class JSONTokener {
|
|||||||
this.back();
|
this.back();
|
||||||
return getJsonArray();
|
return getJsonArray();
|
||||||
default:
|
default:
|
||||||
return getValue(c, strictMode);
|
return nextSimpleValue(c, strictMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is used to get the next value.
|
|
||||||
*
|
|
||||||
* @param c The next character in the JSONTokener.
|
|
||||||
* @param strictMode If true, the method will strictly adhere to the JSON syntax, throwing a JSONException if the
|
|
||||||
* value is not surrounded by quotes.
|
|
||||||
* @return An object which is the next value in the JSONTokener.
|
|
||||||
* @throws JSONException If the value is not surrounded by quotes when strictMode is true.
|
|
||||||
*/
|
|
||||||
private Object getValue(char c, boolean strictMode) {
|
|
||||||
if (strictMode) {
|
|
||||||
Object valueToValidate = nextSimpleValue(c, true);
|
|
||||||
|
|
||||||
boolean isNumeric = checkIfValueIsNumeric(valueToValidate);
|
|
||||||
|
|
||||||
if (isNumeric) {
|
|
||||||
return valueToValidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasQuotes = valueIsWrappedByQuotes(valueToValidate);
|
|
||||||
|
|
||||||
if (!hasQuotes) {
|
|
||||||
throw new JSONException("Value is not surrounded by quotes: " + valueToValidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToValidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nextSimpleValue(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkIfValueIsNumeric(Object valueToValidate) {
|
|
||||||
for (char c : valueToValidate.toString().toCharArray()) {
|
|
||||||
if (!Character.isDigit(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to get a JSONObject from the JSONTokener. The strictMode parameter controls the behavior of
|
* This method is used to get a JSONObject from the JSONTokener. The strictMode parameter controls the behavior of
|
||||||
* the method when parsing the JSONObject.
|
* the method when parsing the JSONObject.
|
||||||
@ -502,38 +473,12 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method checks if the provided value is wrapped by quotes.
|
|
||||||
*
|
|
||||||
* @param valueToValidate The value to be checked. It is converted to a string before checking.
|
|
||||||
* @return A boolean indicating whether the value is wrapped by quotes. It returns true if the value is wrapped by
|
|
||||||
* either single or double quotes.
|
|
||||||
*/
|
|
||||||
private boolean valueIsWrappedByQuotes(Object valueToValidate) {
|
|
||||||
String stringToValidate = valueToValidate.toString();
|
|
||||||
boolean isWrappedByDoubleQuotes = isWrappedByQuotes(stringToValidate, "\"");
|
|
||||||
boolean isWrappedBySingleQuotes = isWrappedByQuotes(stringToValidate, "'");
|
|
||||||
return isWrappedByDoubleQuotes || isWrappedBySingleQuotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isWrappedByQuotes(String valueToValidate, String quoteType) {
|
|
||||||
return valueToValidate.startsWith(quoteType) && valueToValidate.endsWith(quoteType);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object nextSimpleValue(char c) {
|
|
||||||
return nextSimpleValue(c, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object nextSimpleValue(char c, boolean strictMode) {
|
Object nextSimpleValue(char c, boolean strictMode) {
|
||||||
if (c == '"' || c == '\'') {
|
if (c == '"' || c == '\'') {
|
||||||
String str = this.nextString(c);
|
return this.nextString(c, strictMode);
|
||||||
if (strictMode) {
|
|
||||||
return String.format("\"%s\"", str);
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedUnquotedText(c);
|
return parsedUnquotedText(c, strictMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -545,7 +490,7 @@ public class JSONTokener {
|
|||||||
* @return The parsed object.
|
* @return The parsed object.
|
||||||
* @throws JSONException If the parsed string is empty.
|
* @throws JSONException If the parsed string is empty.
|
||||||
*/
|
*/
|
||||||
private Object parsedUnquotedText(char c) {
|
private Object parsedUnquotedText(char c, boolean strictMode) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
@ -556,12 +501,36 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String string = sb.toString().trim();
|
String string = sb.toString().trim();
|
||||||
|
|
||||||
|
if (strictMode) {
|
||||||
|
boolean isBooleanOrNumeric = checkIfValueIsBooleanOrNumeric(string);
|
||||||
|
|
||||||
|
if (isBooleanOrNumeric) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JSONException(String.format("Value is not surrounded by quotes: %s", string));
|
||||||
|
}
|
||||||
|
|
||||||
if (string.isEmpty()) {
|
if (string.isEmpty()) {
|
||||||
throw this.syntaxError("Missing value");
|
throw this.syntaxError("Missing value");
|
||||||
}
|
}
|
||||||
return JSONObject.stringToValue(string);
|
return JSONObject.stringToValue(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkIfValueIsBooleanOrNumeric(Object valueToValidate) {
|
||||||
|
String stringToValidate = valueToValidate.toString();
|
||||||
|
if (stringToValidate.equals("true") || stringToValidate.equals("false")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Double.parseDouble(stringToValidate);
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skip characters until the next character is the requested character.
|
* Skip characters until the next character is the requested character.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user