From d5ab0aa1dcd5b7b63e4fc9d307218a059fa3864b Mon Sep 17 00:00:00 2001 From: Abimaran Kugathasan Date: Sat, 22 Jan 2022 00:15:03 +0800 Subject: [PATCH 1/6] Added test case for converting String to numerals --- src/test/java/org/json/junit/JSONStringTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/java/org/json/junit/JSONStringTest.java b/src/test/java/org/json/junit/JSONStringTest.java index a19961103..a6268b9ab 100644 --- a/src/test/java/org/json/junit/JSONStringTest.java +++ b/src/test/java/org/json/junit/JSONStringTest.java @@ -339,6 +339,22 @@ public void testNullStringValue() throws Exception { } } + /** + * Test for parsing long numerals from String format + * + */ + + @Test + public void testStringLongValue() throws Exception { + JSONObject json = new JSONObject("{\"number_1\":\"01234\", \"number_2\": \"332211\"}"); + + assertEquals(1234L, json.getLong("number_1")); + assertEquals(1234L, json.optLong("number_1")); + assertEquals(332211L, json.getLong("number_2")); + assertEquals(332211L, json.optLong("number_2")); + } + + /** * A JSONString that returns a valid JSON string value. */ From f07104e6d0cf4de0f26edb17d46c6241badf0e6a Mon Sep 17 00:00:00 2001 From: Abimaran Kugathasan Date: Wed, 2 Feb 2022 14:53:31 +0800 Subject: [PATCH 2/6] Refactored to be consistent between opt/get Numerals --- src/main/java/org/json/JSONObject.java | 65 +++++++++----------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 99a075069..e9e25a52c 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -2216,61 +2216,42 @@ protected static boolean isDecimalNotation(final String val) { * caller should catch this and wrap it in a {@link JSONException} if applicable. */ protected static Number stringToNumber(final String val) throws NumberFormatException { - char initial = val.charAt(0); - if ((initial >= '0' && initial <= '9') || initial == '-') { + String trimmedVal = val.trim(); + char initial = trimmedVal.charAt(0); + if (trimmedVal.matches("-?\\d?(\\.\\d+)?")) { // decimal representation - if (isDecimalNotation(val)) { + if (isDecimalNotation(trimmedVal)) { // Use a BigDecimal all the time so we keep the original // representation. BigDecimal doesn't support -0.0, ensure we // keep that by forcing a decimal. try { - BigDecimal bd = new BigDecimal(val); + BigDecimal bd = new BigDecimal(trimmedVal); if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) { - return Double.valueOf(-0.0); + return -0.0; } return bd; } catch (NumberFormatException retryAsDouble) { - // this is to support "Hex Floats" like this: 0x1.0P-1074 - try { - Double d = Double.valueOf(val); - if(d.isNaN() || d.isInfinite()) { - throw new NumberFormatException("val ["+val+"] is not a valid number."); - } - return d; - } catch (NumberFormatException ignore) { - throw new NumberFormatException("val ["+val+"] is not a valid number."); - } - } - } - // block items like 00 01 etc. Java number parsers treat these as Octal. - if(initial == '0' && val.length() > 1) { - char at1 = val.charAt(1); - if(at1 >= '0' && at1 <= '9') { throw new NumberFormatException("val ["+val+"] is not a valid number."); } - } else if (initial == '-' && val.length() > 2) { - char at1 = val.charAt(1); - char at2 = val.charAt(2); - if(at1 == '0' && at2 >= '0' && at2 <= '9') { - throw new NumberFormatException("val ["+val+"] is not a valid number."); + } else { + // integer representation. + // This will narrow any values to the smallest reasonable Object representation + // (Integer, Long, or BigInteger) + + // BigInteger down conversion: We use a similar bitLength compare as + // BigInteger#intValueExact uses. Increases GC, but objects hold + // only what they need. i.e. Less runtime overhead if the value is + // long lived. + + BigInteger bi = new BigInteger(trimmedVal); + if(bi.bitLength() <= 31){ + return bi.intValue(); } + if(bi.bitLength() <= 63){ + return bi.longValue(); + } + return bi; } - // integer representation. - // This will narrow any values to the smallest reasonable Object representation - // (Integer, Long, or BigInteger) - - // BigInteger down conversion: We use a similar bitLength compare as - // BigInteger#intValueExact uses. Increases GC, but objects hold - // only what they need. i.e. Less runtime overhead if the value is - // long lived. - BigInteger bi = new BigInteger(val); - if(bi.bitLength() <= 31){ - return Integer.valueOf(bi.intValue()); - } - if(bi.bitLength() <= 63){ - return Long.valueOf(bi.longValue()); - } - return bi; } throw new NumberFormatException("val ["+val+"] is not a valid number."); } From ba2eceefca916bcf10918dce9d972ec406492549 Mon Sep 17 00:00:00 2001 From: Abimaran Kugathasan Date: Wed, 2 Feb 2022 14:54:13 +0800 Subject: [PATCH 3/6] Added testcases for converting String to int/long/double --- .../java/org/json/junit/JSONStringTest.java | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/json/junit/JSONStringTest.java b/src/test/java/org/json/junit/JSONStringTest.java index a6268b9ab..68f5f9976 100644 --- a/src/test/java/org/json/junit/JSONStringTest.java +++ b/src/test/java/org/json/junit/JSONStringTest.java @@ -346,12 +346,49 @@ public void testNullStringValue() throws Exception { @Test public void testStringLongValue() throws Exception { - JSONObject json = new JSONObject("{\"number_1\":\"01234\", \"number_2\": \"332211\"}"); + JSONObject json = new JSONObject("{\"num1\":\"01234\", \"num2\": \"332211\", \"num3\":\"0987\"}"); + + assertEquals(1234L, json.getLong("num1")); + assertEquals(1234L, json.optLong("num1")); + assertEquals(332211L, json.getLong("num2")); + assertEquals(332211L, json.optLong("num2")); + assertEquals(987, json.getLong("num3")); + assertEquals(987, json.optLong("num3")); + } + + + /** + * Test for parsing int numerals from String format + * + */ + + @Test + public void testStringIntValue() throws Exception { + JSONObject json = new JSONObject("{\"num1\":\"01234\", \"num2\": \"332211\", \"num3\":\"0987\"}"); + + assertEquals(1234, json.getInt("num1")); + assertEquals(1234, json.optInt("num1")); + assertEquals(332211, json.getInt("num2")); + assertEquals(332211, json.optInt("num2")); + assertEquals(987, json.getInt("num3")); + assertEquals(987, json.optInt("num3")); + } - assertEquals(1234L, json.getLong("number_1")); - assertEquals(1234L, json.optLong("number_1")); - assertEquals(332211L, json.getLong("number_2")); - assertEquals(332211L, json.optLong("number_2")); + /** + * Test for parsing double numerals from String format + * + */ + + @Test + public void testStringDoubleValue() throws Exception { + JSONObject json = new JSONObject("{\"num1\":\".123\", \"num2\": \"0.123\", \"num3\":\"8.123\"}"); + + //assertEquals(.123, json.getDouble("num1"), 0); + assertEquals(.123, json.optDouble("num1"), 0); + assertEquals(0.123, json.getDouble("num2"), 0); + assertEquals(0.123, json.optDouble("num2"), 0); + assertEquals(8.123, json.getDouble("num3"), 0); + assertEquals(8.123, json.optDouble("num3"), 0); } From ac02e459f959eafab8b9d24bd2961ddc064c6bf3 Mon Sep 17 00:00:00 2001 From: Abimaran Kugathasan Date: Wed, 2 Feb 2022 15:51:03 +0800 Subject: [PATCH 4/6] Added testcase for get/opt Float values --- .../java/org/json/junit/JSONStringTest.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/json/junit/JSONStringTest.java b/src/test/java/org/json/junit/JSONStringTest.java index 68f5f9976..ced808ca7 100644 --- a/src/test/java/org/json/junit/JSONStringTest.java +++ b/src/test/java/org/json/junit/JSONStringTest.java @@ -383,7 +383,7 @@ public void testStringIntValue() throws Exception { public void testStringDoubleValue() throws Exception { JSONObject json = new JSONObject("{\"num1\":\".123\", \"num2\": \"0.123\", \"num3\":\"8.123\"}"); - //assertEquals(.123, json.getDouble("num1"), 0); + assertEquals(.123, json.getDouble("num1"), 0); assertEquals(.123, json.optDouble("num1"), 0); assertEquals(0.123, json.getDouble("num2"), 0); assertEquals(0.123, json.optDouble("num2"), 0); @@ -391,6 +391,22 @@ public void testStringDoubleValue() throws Exception { assertEquals(8.123, json.optDouble("num3"), 0); } + /** + * Test for parsing float numerals from String format + */ + + @Test + public void testStringFloatValue() throws Exception { + JSONObject json = new JSONObject("{\"num1\":\".123\", \"num2\": \"0.123\", \"num3\":\"8.123\"}"); + + assertEquals(.123, json.getFloat("num1"), 0.000001); + assertEquals(.123, json.optFloat("num1"), 0.000001); + assertEquals(0.123, json.getFloat("num2"), 0.000001); + assertEquals(0.123, json.optFloat("num2"), 0.000001); + assertEquals(8.123, json.getFloat("num3"), 0.000001); + assertEquals(8.123, json.optFloat("num3"), 0.000001); + } + /** * A JSONString that returns a valid JSON string value. From 2f37f5be6de5fde1a0e687939679b2bf9cd0899e Mon Sep 17 00:00:00 2001 From: Abimaran Kugathasan Date: Wed, 2 Feb 2022 16:13:05 +0800 Subject: [PATCH 5/6] Added testcase for get/opt BigDecimal and BigInteger values --- .../java/org/json/junit/JSONStringTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/java/org/json/junit/JSONStringTest.java b/src/test/java/org/json/junit/JSONStringTest.java index ced808ca7..553cda8c2 100644 --- a/src/test/java/org/json/junit/JSONStringTest.java +++ b/src/test/java/org/json/junit/JSONStringTest.java @@ -27,6 +27,8 @@ of this software and associated documentation files (the "Software"), to deal import static org.junit.Assert.*; import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.*; import org.json.*; @@ -408,6 +410,39 @@ public void testStringFloatValue() throws Exception { } + /** + * Test for parsing BigDecimal numerals from String format + * + */ + + @Test + public void testStringBigDecimalValue() throws Exception { + JSONObject json = new JSONObject("{\"num1\":\".123\", \"num2\": \"0.123\", \"num3\":\"8.123\"}"); + + assertEquals(new BigDecimal(".123"), json.getBigDecimal("num1")); + assertEquals(new BigDecimal(".123"), json.optBigDecimal("num1", BigDecimal.ONE)); + assertEquals(new BigDecimal("0.123"), json.getBigDecimal("num2")); + assertEquals(new BigDecimal("0.123"), json.optBigDecimal("num2", BigDecimal.ONE)); + assertEquals(new BigDecimal("8.123"), json.getBigDecimal("num3")); + assertEquals(new BigDecimal("8.123"), json.optBigDecimal("num3", BigDecimal.ONE)); + } + + /** + * Test for parsing BigInteger numerals from String format + */ + + @Test + public void testStringBigIntegerValue() throws Exception { + JSONObject json = new JSONObject("{\"num1\":\"01234\", \"num2\": \"332211\", \"num3\":\"0987\"}"); + + assertEquals(BigInteger.valueOf(1234), json.getBigInteger("num1")); + assertEquals(BigInteger.valueOf(1234), json.getBigInteger("num1")); + assertEquals(BigInteger.valueOf(332211), json.getBigInteger("num2")); + assertEquals(BigInteger.valueOf(332211), json.getBigInteger("num2")); + assertEquals(BigInteger.valueOf(987), json.getBigInteger("num3")); + assertEquals(BigInteger.valueOf(987), json.getBigInteger("num3")); + } + /** * A JSONString that returns a valid JSON string value. */ From 59c4f1295407551af3ab26d556dbd6e801793a7a Mon Sep 17 00:00:00 2001 From: Abimaran Kugathasan Date: Sun, 6 Feb 2022 14:30:43 +0800 Subject: [PATCH 6/6] Validated if the given String is Numberic using Java's native APIs --- src/main/java/org/json/JSONObject.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index f73325b51..013be194c 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -36,6 +36,8 @@ of this software and associated documentation files (the "Software"), to deal import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; +import java.text.NumberFormat; +import java.text.ParsePosition; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; @@ -2225,7 +2227,7 @@ protected static boolean isDecimalNotation(final String val) { protected static Number stringToNumber(final String val) throws NumberFormatException { String trimmedVal = val.trim(); char initial = trimmedVal.charAt(0); - if (trimmedVal.matches("-?\\d?(\\.\\d+)?")) { + if (isNumeric(trimmedVal)) { // decimal representation if (isDecimalNotation(trimmedVal)) { // Use a BigDecimal all the time so we keep the original @@ -2710,4 +2712,10 @@ private static JSONException recursivelyDefinedObjectException(String key) { "JavaBean object contains recursively defined member variable of key " + quote(key) ); } + + private static boolean isNumeric(String str) { + ParsePosition pos = new ParsePosition(0); + NumberFormat.getInstance().parse(str, pos); + return str.length() == pos.getIndex(); + } }