From 3a8193bea4c096c5999f27a9b20d7f087e7efd06 Mon Sep 17 00:00:00 2001 From: "John J. Aylward" Date: Thu, 19 Nov 2020 18:18:02 -0500 Subject: [PATCH 1/4] upgrade junit version --- build.gradle | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 64f87d2..77f9eee 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ repositories { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.1' testImplementation 'com.jayway.jsonpath:json-path:2.1.0' testImplementation 'org.mockito:mockito-core:1.9.5' } diff --git a/pom.xml b/pom.xml index e70c487..e5449ba 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ junit junit - 4.12 + 4.13.1 test From e4b76d658831391d4ab4e6556668e1014850ccba Mon Sep 17 00:00:00 2001 From: "John J. Aylward" Date: Thu, 19 Nov 2020 18:18:27 -0500 Subject: [PATCH 2/4] Add test to demonstrate the issue. See #573 --- src/test/java/org/json/junit/JSONObjectTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 51407f0..374bc10 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -115,10 +115,17 @@ public class JSONObjectTest { .put("key2", 2) .put("key3", new String(string1)); - assertFalse("Should eval to false", obj1.similar(obj2)); + JSONObject obj4 = new JSONObject() + .put("key1", "abc") + .put("key2", 2.0) + .put("key3", new String(string1)); + assertFalse("Should eval to false", obj1.similar(obj2)); + assertTrue("Should eval to true", obj1.similar(obj3)); + assertTrue("Should eval to true", obj1.similar(obj4)); + } @Test From 11e6b1af7ead146eaf06e57df5459363311a8d69 Mon Sep 17 00:00:00 2001 From: "John J. Aylward" Date: Thu, 19 Nov 2020 18:24:56 -0500 Subject: [PATCH 3/4] fixes issue #573 by added specific compare of numeric types --- src/main/java/org/json/JSONArray.java | 2 + src/main/java/org/json/JSONObject.java | 53 +++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/json/JSONArray.java b/src/main/java/org/json/JSONArray.java index f11b328..338177c 100644 --- a/src/main/java/org/json/JSONArray.java +++ b/src/main/java/org/json/JSONArray.java @@ -1374,6 +1374,8 @@ public class JSONArray implements Iterable { if (!((JSONArray)valueThis).similar(valueOther)) { return false; } + } else if (valueThis instanceof Number && valueOther instanceof Number) { + return JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther); } else if (!valueThis.equals(valueOther)) { return false; } diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index f718c06..72ecee5 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -2073,6 +2073,8 @@ public class JSONObject { if (!((JSONArray)valueThis).similar(valueOther)) { return false; } + } else if (valueThis instanceof Number && valueOther instanceof Number) { + return isNumberSimilar((Number)valueThis, (Number)valueOther); } else if (!valueThis.equals(valueOther)) { return false; } @@ -2083,6 +2085,55 @@ public class JSONObject { } } + /** + * Compares two numbers to see if they are similar. + * + * If either of the numbers are Double or Float instances, then they are checked to have + * a finite value. If either value is not finite (NaN or ±infinity), then this + * function will always return false. If both numbers are finite, they are first checked + * to be the same type and implement {@link Comparable}. If they do, then the actual + * {@link Comparable#compareTo(Object)} is called. If they are not the same type, or don't + * implement Comparable, then they are converted to {@link BigDecimal}s. Finally the + * BigDecimal values are compared using {@link BigDecimal#compareTo(BigDecimal)}. + * + * @param l the Left value to compare. Can not be null. + * @param r the right value to compare. Can not be null. + * @return true if the numbers are similar, false otherwise. + */ + static boolean isNumberSimilar(Number l, Number r) { + if (!numberIsFinite(l) || !numberIsFinite(r)) { + // non-finite numbers are never similar + return false; + } + + // if the classes are the same and implement Comparable + // then use the built in compare first. + if(l.getClass().equals(r.getClass()) && l instanceof Comparable) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + int compareTo = ((Comparable)l).compareTo(r); + return compareTo==0; + } + + // BigDecimal should be able to handle all of our number types that we support through + // documentation. Convert to BigDecimal first, then use the Compare method to + // decide equality. + final BigDecimal lBigDecimal = objectToBigDecimal(l, null); + final BigDecimal rBigDecimal = objectToBigDecimal(r, null); + if (lBigDecimal == null || rBigDecimal == null) { + return false; + } + return lBigDecimal.compareTo(rBigDecimal) == 0; + } + + private static boolean numberIsFinite(Number n) { + if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) { + return false; + } else if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) { + return false; + } + return true; + } + /** * Tests if the value should be tried as a decimal. It makes no test if there are actual digits. * @@ -2354,7 +2405,7 @@ public class JSONObject { */ public static Object wrap(Object object) { try { - if (object == null) { + if (NULL.equals(object)) { return NULL; } if (object instanceof JSONObject || object instanceof JSONArray From 68883b9ff822ad78d889864c48ac3141d90175d5 Mon Sep 17 00:00:00 2001 From: "John J. Aylward" Date: Thu, 19 Nov 2020 19:10:08 -0500 Subject: [PATCH 4/4] update number handling to use new helper method for consistency. --- src/main/java/org/json/JSONObject.java | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 72ecee5..b838b8e 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -1161,8 +1161,7 @@ public class JSONObject { return new BigDecimal((BigInteger) val); } if (val instanceof Double || val instanceof Float){ - final double d = ((Number) val).doubleValue(); - if(Double.isNaN(d)) { + if (!numberIsFinite((Number)val)) { return defaultValue; } return new BigDecimal(((Number) val).doubleValue()); @@ -1212,11 +1211,10 @@ public class JSONObject { return ((BigDecimal) val).toBigInteger(); } if (val instanceof Double || val instanceof Float){ - final double d = ((Number) val).doubleValue(); - if(Double.isNaN(d)) { + if (!numberIsFinite((Number)val)) { return defaultValue; } - return new BigDecimal(d).toBigInteger(); + return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); } if (val instanceof Long || val instanceof Integer || val instanceof Short || val instanceof Byte){ @@ -2267,18 +2265,8 @@ public class JSONObject { * If o is a non-finite number. */ public static void testValidity(Object o) throws JSONException { - if (o != null) { - if (o instanceof Double) { - if (((Double) o).isInfinite() || ((Double) o).isNaN()) { - throw new JSONException( - "JSON does not allow non-finite numbers."); - } - } else if (o instanceof Float) { - if (((Float) o).isInfinite() || ((Float) o).isNaN()) { - throw new JSONException( - "JSON does not allow non-finite numbers."); - } - } + if (o instanceof Number && !numberIsFinite((Number) o)) { + throw new JSONException("JSON does not allow non-finite numbers."); } }