Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 125 additions & 71 deletions src/main/java/org/json/JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,7 @@ static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) {
if (!numberIsFinite((Number)val)) {
return defaultValue;
}
return new BigDecimal(((Number) val).doubleValue()).toBigInteger();
return BigDecimal.valueOf(((Number) val).doubleValue()).toBigInteger();
}
if (val instanceof Long || val instanceof Integer
|| val instanceof Short || val instanceof Byte){
Expand Down Expand Up @@ -2041,7 +2041,7 @@ private static int getAnnotationDepth(final Method m, final Class<? extends Anno
return 1;
}

// if we've already reached the Object class, return -1;
// since we've already reached the Object class, return -1;
Class<?> c = m.getDeclaringClass();
if (c.getSuperclass() == null) {
return -1;
Expand All @@ -2057,9 +2057,9 @@ private static int getAnnotationDepth(final Method m, final Class<? extends Anno
return d + 1;
}
} catch (final SecurityException ex) {
continue;
// Nothing to do here
} catch (final NoSuchMethodException ex) {
continue;
// Nothing to do here
}
}

Expand Down Expand Up @@ -2427,21 +2427,32 @@ public static Writer quote(String string, Writer w) throws IOException {
w.write("\\r");
break;
default:
if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
|| (c >= '\u2000' && c < '\u2100')) {
w.write("\\u");
hhhh = Integer.toHexString(c);
w.write("0000", 0, 4 - hhhh.length());
w.write(hhhh);
} else {
w.write(c);
}
writeAsHex(w, c);
}
}
w.write('"');
return w;
}

/**
* Convenience method to reduce cognitive complexity of quote()
* @param w The Writer to which the quoted string will be appended.
* @param c Character to write
* @throws IOException
*/
private static void writeAsHex(Writer w, char c) throws IOException {
String hhhh;
if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
|| (c >= '\u2000' && c < '\u2100')) {
w.write("\\u");
hhhh = Integer.toHexString(c);
w.write("0000", 0, 4 - hhhh.length());
w.write(hhhh);
} else {
w.write(c);
}
}

/**
* Remove a name and its value, if present.
*
Expand Down Expand Up @@ -2470,42 +2481,46 @@ public boolean similar(Object other) {
if (!this.keySet().equals(((JSONObject)other).keySet())) {
return false;
}
for (final Entry<String,?> entry : this.entrySet()) {
String name = entry.getKey();
Object valueThis = entry.getValue();
Object valueOther = ((JSONObject)other).get(name);
if(valueThis == valueOther) {
continue;
}
if(valueThis == null) {
return false;
}
if (valueThis instanceof JSONObject) {
if (!((JSONObject)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof JSONArray) {
if (!((JSONArray)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof Number && valueOther instanceof Number) {
if (!isNumberSimilar((Number)valueThis, (Number)valueOther)) {
return false;
}
} else if (valueThis instanceof JSONString && valueOther instanceof JSONString) {
if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) {
return false;
}
} else if (!valueThis.equals(valueOther)) {
return false;
}
}
return true;
return checkSimilarEntries(other);
} catch (Throwable exception) {
return false;
}
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add JavaDocs for this method

private boolean checkSimilarEntries(Object other) {
for (final Entry<String,?> entry : this.entrySet()) {
String name = entry.getKey();
Object valueThis = entry.getValue();
Object valueOther = ((JSONObject)other).get(name);
if(valueThis == valueOther) {
continue;
}
if(valueThis == null) {
return false;
}

if (!checkThis(valueThis, valueOther)) {
return false;
}
}
return true;
}

private boolean checkThis(Object valueThis, Object valueOther) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add JavaDocs for this method

if (valueThis instanceof JSONObject) {
return ((JSONObject)valueThis).similar(valueOther);
} else if (valueThis instanceof JSONArray) {
return ((JSONArray)valueThis).similar(valueOther);
} else if (valueThis instanceof Number && valueOther instanceof Number) {
return isNumberSimilar((Number)valueThis, (Number)valueOther);
} else if (valueThis instanceof JSONString && valueOther instanceof JSONString) {
return ((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString());
} else if (!valueThis.equals(valueOther)) {
return false;
}
return true;
}

/**
* Compares two numbers to see if they are similar.
*
Expand Down Expand Up @@ -2911,28 +2926,15 @@ static final Writer writeValue(Writer writer, Object value,
if (value == null || value.equals(null)) {
writer.write("null");
} else if (value instanceof JSONString) {
// JSONString must be checked first, so it can overwrite behaviour of other types below
Object o;
try {
o = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
writer.write(o != null ? o.toString() : quote(value.toString()));
// may throw an exception
processJsonStringToWriteValue(writer, value);
} else if (value instanceof String) {
// assuming most values are Strings, so testing it early
quote(value.toString(), writer);
return writer;
} else if (value instanceof Number) {
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
final String numberAsString = numberToString((Number) value);
if(NUMBER_PATTERN.matcher(numberAsString).matches()) {
writer.write(numberAsString);
} else {
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
quote(numberAsString, writer);
}
// may throw an exception
processNumberToWriteValue(writer, (Number) value);
} else if (value instanceof Boolean) {
writer.write(value.toString());
} else if (value instanceof Enum<?>) {
Expand All @@ -2955,6 +2957,41 @@ static final Writer writeValue(Writer writer, Object value,
return writer;
}

/**
* Convenience function to reduce cog complexity of calling method; writes value if string is valid
* @param writer Object doing the writing
* @param value Value to be written
* @throws IOException if something goes wrong
*/
private static void processJsonStringToWriteValue(Writer writer, Object value) throws IOException {
// JSONString must be checked first, so it can overwrite behaviour of other types below
Object o;
try {
o = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
writer.write(o != null ? o.toString() : quote(value.toString()));
}

/**
* Convenience function to reduce cog complexity of calling method; writes value if number is valid
* @param writer Object doing the writing
* @param value Value to be written
* @throws IOException if something goes wrong
*/
private static void processNumberToWriteValue(Writer writer, Number value) throws IOException {
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
final String numberAsString = numberToString(value);
if(NUMBER_PATTERN.matcher(numberAsString).matches()) {
writer.write(numberAsString);
} else {
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
quote(numberAsString, writer);
}
}

static final void indent(Writer writer, int indent) throws IOException {
for (int i = 0; i < indent; i += 1) {
writer.write(' ');
Expand Down Expand Up @@ -3004,11 +3041,8 @@ public Writer write(Writer writer, int indentFactor, int indent)
if (indentFactor > 0) {
writer.write(' ');
}
try{
writeValue(writer, entry.getValue(), indentFactor, indent);
} catch (Exception e) {
throw new JSONException("Unable to write JSONObject value for key: " + key, e);
}
// might throw an exception
attemptWriteValue(writer, indentFactor, indent, entry, key);
} else if (length != 0) {
final int newIndent = indent + indentFactor;
for (final Entry<String,?> entry : this.entrySet()) {
Expand All @@ -3025,11 +3059,7 @@ public Writer write(Writer writer, int indentFactor, int indent)
if (indentFactor > 0) {
writer.write(' ');
}
try {
writeValue(writer, entry.getValue(), indentFactor, newIndent);
} catch (Exception e) {
throw new JSONException("Unable to write JSONObject value for key: " + key, e);
}
attemptWriteValue(writer, indentFactor, newIndent, entry, key);
needsComma = true;
}
if (indentFactor > 0) {
Expand All @@ -3044,6 +3074,30 @@ public Writer write(Writer writer, int indentFactor, int indent)
}
}

/**
* Convenience function. Writer attempts to write a value.
* @param writer
* Writes the serialized JSON
* @param indentFactor
* The number of spaces to add to each level of indentation.
* @param indent
* The indentation of the top level.
* @param entry
* Contains the value being written
* @param key
* Identifies the value
* @throws JSONException if a called function has an error or a write error
* occurs

*/
private static void attemptWriteValue(Writer writer, int indentFactor, int indent, Entry<String, ?> entry, String key) {
try{
writeValue(writer, entry.getValue(), indentFactor, indent);
} catch (Exception e) {
throw new JSONException("Unable to write JSONObject value for key: " + key, e);
}
}

/**
* Returns a java.util.Map containing all of the entries in this object.
* If an entry in the object is a JSONArray or JSONObject it will also
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/json/junit/JSONObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3896,8 +3896,8 @@ public void issue743SerializationMapWith512Objects() {

@Test
public void issue743SerializationMapWith1000Objects() {
HashMap<String, Object> map = buildNestedMap(1000);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(1000);
HashMap<String, Object> map = buildNestedMap(500);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment explaining why the limit was changed. Also, method signature should be updated to '500Objects'.

Not why this testcase started failing in laptop tests.

JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(500);
JSONObject object = new JSONObject(map, parserConfiguration);
String jsonString = object.toString();
}
Expand Down