Skip to content

Commit d899865

Browse files
author
alwinsanil
committed
Missing Hierarchy: JSONArray-JSONObject-JSONString 'similar' method code smell fix
1 parent a835bf3 commit d899865

5 files changed

Lines changed: 85 additions & 59 deletions

File tree

src/main/java/org/json/JSONArray.java

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
* @author JSON.org
6161
* @version 2016-08/15
6262
*/
63-
public class JSONArray implements Iterable<Object> {
63+
public class JSONArray implements Iterable<Object>, JSONSimilar {
6464

6565
/**
6666
* The arrayList where the JSONArray's properties are kept.
@@ -1643,44 +1643,21 @@ public Object remove(int index) {
16431643
/**
16441644
* Determine if two JSONArrays are similar.
16451645
* They must contain similar sequences.
1646-
*
16471646
* @param other The other JSONArray
16481647
* @return true if they are equal
16491648
*/
1649+
@Override
16501650
public boolean similar(Object other) {
16511651
if (!(other instanceof JSONArray)) {
16521652
return false;
16531653
}
1654+
JSONArray otherArray = (JSONArray)other;
16541655
int len = this.length();
1655-
if (len != ((JSONArray)other).length()) {
1656+
if (len != otherArray.length()) {
16561657
return false;
16571658
}
16581659
for (int i = 0; i < len; i += 1) {
1659-
Object valueThis = this.myArrayList.get(i);
1660-
Object valueOther = ((JSONArray)other).myArrayList.get(i);
1661-
if(valueThis == valueOther) {
1662-
continue;
1663-
}
1664-
if(valueThis == null) {
1665-
return false;
1666-
}
1667-
if (valueThis instanceof JSONObject) {
1668-
if (!((JSONObject)valueThis).similar(valueOther)) {
1669-
return false;
1670-
}
1671-
} else if (valueThis instanceof JSONArray) {
1672-
if (!((JSONArray)valueThis).similar(valueOther)) {
1673-
return false;
1674-
}
1675-
} else if (valueThis instanceof Number && valueOther instanceof Number) {
1676-
if (!JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther)) {
1677-
return false;
1678-
}
1679-
} else if (valueThis instanceof JSONString && valueOther instanceof JSONString) {
1680-
if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) {
1681-
return false;
1682-
}
1683-
} else if (!valueThis.equals(valueOther)) {
1660+
if (!JSONSimilar.compare(this.myArrayList.get(i), otherArray.myArrayList.get(i))) {
16841661
return false;
16851662
}
16861663
}

src/main/java/org/json/JSONObject.java

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
* @author JSON.org
7272
* @version 2016-08-15
7373
*/
74-
public class JSONObject {
74+
public class JSONObject implements JSONSimilar {
7575
/**
7676
* JSONObject.NULL is equivalent to the value that JavaScript calls null,
7777
* whilst Java's null is equivalent to the value that JavaScript calls
@@ -2358,45 +2358,21 @@ public Object remove(String key) {
23582358
* Determine if two JSONObjects are similar.
23592359
* They must contain the same set of names which must be associated with
23602360
* similar values.
2361-
*
23622361
* @param other The other JSONObject
23632362
* @return true if they are equal
23642363
*/
2364+
@Override
23652365
public boolean similar(Object other) {
23662366
try {
23672367
if (!(other instanceof JSONObject)) {
23682368
return false;
23692369
}
2370-
if (!this.keySet().equals(((JSONObject)other).keySet())) {
2370+
JSONObject otherObj = (JSONObject)other;
2371+
if (!this.keySet().equals(otherObj.keySet())) {
23712372
return false;
23722373
}
2373-
for (final Entry<String,?> entry : this.entrySet()) {
2374-
String name = entry.getKey();
2375-
Object valueThis = entry.getValue();
2376-
Object valueOther = ((JSONObject)other).get(name);
2377-
if(valueThis == valueOther) {
2378-
continue;
2379-
}
2380-
if(valueThis == null) {
2381-
return false;
2382-
}
2383-
if (valueThis instanceof JSONObject) {
2384-
if (!((JSONObject)valueThis).similar(valueOther)) {
2385-
return false;
2386-
}
2387-
} else if (valueThis instanceof JSONArray) {
2388-
if (!((JSONArray)valueThis).similar(valueOther)) {
2389-
return false;
2390-
}
2391-
} else if (valueThis instanceof Number && valueOther instanceof Number) {
2392-
if (!isNumberSimilar((Number)valueThis, (Number)valueOther)) {
2393-
return false;
2394-
}
2395-
} else if (valueThis instanceof JSONString && valueOther instanceof JSONString) {
2396-
if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) {
2397-
return false;
2398-
}
2399-
} else if (!valueThis.equals(valueOther)) {
2374+
for (Map.Entry<String,?> entry : this.entrySet()) {
2375+
if (!JSONSimilar.compare(entry.getValue(), otherObj.get(entry.getKey()))) {
24002376
return false;
24012377
}
24022378
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.json;
2+
3+
/**
4+
* Interface for comparing JSON entities for semantic similarity.
5+
* @author JSON.org
6+
* @version 2023-07-20
7+
*/
8+
public interface JSONSimilar {
9+
/**
10+
* Determine if this JSON entity is similar to another object.
11+
* @param other The object to compare with
12+
* @return true if they are semantically similar
13+
*/
14+
boolean similar(Object other);
15+
16+
/**
17+
* Helper method to compare two arbitrary values according to JSON similarity rules.
18+
* @param a First value to compare
19+
* @param b Second value to compare
20+
* @return true if values are semantically similar
21+
*/
22+
static boolean compare(Object a, Object b) {
23+
if (a == b) return true;
24+
if (a == null || b == null) return false;
25+
if (a instanceof JSONSimilar) {
26+
return ((JSONSimilar)a).similar(b);
27+
}
28+
if (a instanceof Number && b instanceof Number) {
29+
return JSONSimilarUtils.areNumbersSimilar((Number)a, (Number)b);
30+
}
31+
return a.equals(b);
32+
}
33+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.json;
2+
3+
/**
4+
* Utility class for JSON similarity comparisons.
5+
* @author JSON.org
6+
* @version 2023-07-20
7+
*/
8+
public final class JSONSimilarUtils {
9+
private static final double FLOATING_POINT_TOLERANCE = 0.000001d;
10+
11+
/**
12+
* Compare two numbers for JSON similarity with proper handling of different numeric types.
13+
* @param a First number to compare
14+
* @param b Second number to compare
15+
* @return true if numbers are semantically equivalent in JSON context
16+
*/
17+
static boolean areNumbersSimilar(Number a, Number b) {
18+
if (a.equals(b)) {
19+
return true;
20+
}
21+
if (a instanceof Double || a instanceof Float ||
22+
b instanceof Double || b instanceof Float) {
23+
return Math.abs(a.doubleValue() - b.doubleValue()) < FLOATING_POINT_TOLERANCE;
24+
}
25+
return a.longValue() == b.longValue();
26+
}
27+
}

src/main/java/org/json/JSONString.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,25 @@
1212
* <code>toJSONString</code> method will be used instead of the default behavior
1313
* of using the Object's <code>toString()</code> method and quoting the result.
1414
*/
15-
public interface JSONString {
15+
public interface JSONString extends JSONSimilar {
1616
/**
1717
* The <code>toJSONString</code> method allows a class to produce its own JSON
1818
* serialization.
1919
*
2020
* @return A strictly syntactically correct JSON text.
2121
*/
2222
public String toJSONString();
23+
24+
/**
25+
* Determine if two JSONStrings are similar by comparing their serialized forms.
26+
* @param other The other JSONString
27+
* @return true if their JSON representations are equal
28+
*/
29+
@Override
30+
default boolean similar(Object other) {
31+
if (!(other instanceof JSONString)) {
32+
return false;
33+
}
34+
return this.toJSONString().equals(((JSONString)other).toJSONString());
35+
}
2336
}

0 commit comments

Comments
 (0)