Skip to content

Commit a57eff2

Browse files
authored
Merge pull request #575 from johnjaylward/fix-similar-compare-numbers
Fix similar compare numbers
2 parents 6bf2692 + 68883b9 commit a57eff2

5 files changed

Lines changed: 69 additions & 21 deletions

File tree

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ repositories {
2323
}
2424

2525
dependencies {
26-
testImplementation 'junit:junit:4.12'
26+
testImplementation 'junit:junit:4.13.1'
2727
testImplementation 'com.jayway.jsonpath:json-path:2.1.0'
2828
testImplementation 'org.mockito:mockito-core:1.9.5'
2929
}

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
<dependency>
8181
<groupId>junit</groupId>
8282
<artifactId>junit</artifactId>
83-
<version>4.12</version>
83+
<version>4.13.1</version>
8484
<scope>test</scope>
8585
</dependency>
8686
<dependency>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,8 @@ public boolean similar(Object other) {
13741374
if (!((JSONArray)valueThis).similar(valueOther)) {
13751375
return false;
13761376
}
1377+
} else if (valueThis instanceof Number && valueOther instanceof Number) {
1378+
return JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther);
13771379
} else if (!valueThis.equals(valueOther)) {
13781380
return false;
13791381
}

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

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,8 +1161,7 @@ static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) {
11611161
return new BigDecimal((BigInteger) val);
11621162
}
11631163
if (val instanceof Double || val instanceof Float){
1164-
final double d = ((Number) val).doubleValue();
1165-
if(Double.isNaN(d)) {
1164+
if (!numberIsFinite((Number)val)) {
11661165
return defaultValue;
11671166
}
11681167
return new BigDecimal(((Number) val).doubleValue());
@@ -1212,11 +1211,10 @@ static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) {
12121211
return ((BigDecimal) val).toBigInteger();
12131212
}
12141213
if (val instanceof Double || val instanceof Float){
1215-
final double d = ((Number) val).doubleValue();
1216-
if(Double.isNaN(d)) {
1214+
if (!numberIsFinite((Number)val)) {
12171215
return defaultValue;
12181216
}
1219-
return new BigDecimal(d).toBigInteger();
1217+
return new BigDecimal(((Number) val).doubleValue()).toBigInteger();
12201218
}
12211219
if (val instanceof Long || val instanceof Integer
12221220
|| val instanceof Short || val instanceof Byte){
@@ -2073,6 +2071,8 @@ public boolean similar(Object other) {
20732071
if (!((JSONArray)valueThis).similar(valueOther)) {
20742072
return false;
20752073
}
2074+
} else if (valueThis instanceof Number && valueOther instanceof Number) {
2075+
return isNumberSimilar((Number)valueThis, (Number)valueOther);
20762076
} else if (!valueThis.equals(valueOther)) {
20772077
return false;
20782078
}
@@ -2083,6 +2083,55 @@ public boolean similar(Object other) {
20832083
}
20842084
}
20852085

2086+
/**
2087+
* Compares two numbers to see if they are similar.
2088+
*
2089+
* If either of the numbers are Double or Float instances, then they are checked to have
2090+
* a finite value. If either value is not finite (NaN or &#177;infinity), then this
2091+
* function will always return false. If both numbers are finite, they are first checked
2092+
* to be the same type and implement {@link Comparable}. If they do, then the actual
2093+
* {@link Comparable#compareTo(Object)} is called. If they are not the same type, or don't
2094+
* implement Comparable, then they are converted to {@link BigDecimal}s. Finally the
2095+
* BigDecimal values are compared using {@link BigDecimal#compareTo(BigDecimal)}.
2096+
*
2097+
* @param l the Left value to compare. Can not be <code>null</code>.
2098+
* @param r the right value to compare. Can not be <code>null</code>.
2099+
* @return true if the numbers are similar, false otherwise.
2100+
*/
2101+
static boolean isNumberSimilar(Number l, Number r) {
2102+
if (!numberIsFinite(l) || !numberIsFinite(r)) {
2103+
// non-finite numbers are never similar
2104+
return false;
2105+
}
2106+
2107+
// if the classes are the same and implement Comparable
2108+
// then use the built in compare first.
2109+
if(l.getClass().equals(r.getClass()) && l instanceof Comparable) {
2110+
@SuppressWarnings({ "rawtypes", "unchecked" })
2111+
int compareTo = ((Comparable)l).compareTo(r);
2112+
return compareTo==0;
2113+
}
2114+
2115+
// BigDecimal should be able to handle all of our number types that we support through
2116+
// documentation. Convert to BigDecimal first, then use the Compare method to
2117+
// decide equality.
2118+
final BigDecimal lBigDecimal = objectToBigDecimal(l, null);
2119+
final BigDecimal rBigDecimal = objectToBigDecimal(r, null);
2120+
if (lBigDecimal == null || rBigDecimal == null) {
2121+
return false;
2122+
}
2123+
return lBigDecimal.compareTo(rBigDecimal) == 0;
2124+
}
2125+
2126+
private static boolean numberIsFinite(Number n) {
2127+
if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) {
2128+
return false;
2129+
} else if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) {
2130+
return false;
2131+
}
2132+
return true;
2133+
}
2134+
20862135
/**
20872136
* Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
20882137
*
@@ -2216,18 +2265,8 @@ public static Object stringToValue(String string) {
22162265
* If o is a non-finite number.
22172266
*/
22182267
public static void testValidity(Object o) throws JSONException {
2219-
if (o != null) {
2220-
if (o instanceof Double) {
2221-
if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
2222-
throw new JSONException(
2223-
"JSON does not allow non-finite numbers.");
2224-
}
2225-
} else if (o instanceof Float) {
2226-
if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
2227-
throw new JSONException(
2228-
"JSON does not allow non-finite numbers.");
2229-
}
2230-
}
2268+
if (o instanceof Number && !numberIsFinite((Number) o)) {
2269+
throw new JSONException("JSON does not allow non-finite numbers.");
22312270
}
22322271
}
22332272

@@ -2354,7 +2393,7 @@ public static String valueToString(Object value) throws JSONException {
23542393
*/
23552394
public static Object wrap(Object object) {
23562395
try {
2357-
if (object == null) {
2396+
if (NULL.equals(object)) {
23582397
return NULL;
23592398
}
23602399
if (object instanceof JSONObject || object instanceof JSONArray

src/test/java/org/json/junit/JSONObjectTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,17 @@ public void verifySimilar() {
115115
.put("key2", 2)
116116
.put("key3", new String(string1));
117117

118-
assertFalse("Should eval to false", obj1.similar(obj2));
118+
JSONObject obj4 = new JSONObject()
119+
.put("key1", "abc")
120+
.put("key2", 2.0)
121+
.put("key3", new String(string1));
119122

123+
assertFalse("Should eval to false", obj1.similar(obj2));
124+
120125
assertTrue("Should eval to true", obj1.similar(obj3));
121126

127+
assertTrue("Should eval to true", obj1.similar(obj4));
128+
122129
}
123130

124131
@Test

0 commit comments

Comments
 (0)