Skip to content

Commit

Permalink
stleary#653 - review comments updated.
Browse files Browse the repository at this point in the history
  • Loading branch information
rudrajyotib committed Oct 11, 2023
1 parent d5277b1 commit 0cdc38a
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 34 deletions.
66 changes: 41 additions & 25 deletions src/main/java/org/json/JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -2392,8 +2392,14 @@ protected static boolean isDecimalNotation(final String val) {
*/
protected static Number stringToNumber(final String input) throws NumberFormatException {
String val = input;
if (val.startsWith(".")){
val = "0"+val;
}
if (val.startsWith("-.")){
val = "-0."+val.substring(2);
}
char initial = val.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
if ((initial >= '0' && initial <= '9') || initial == '-' ) {
// decimal representation
if (isDecimalNotation(val)) {
// Use a BigDecimal all the time so we keep the original
Expand Down Expand Up @@ -2424,13 +2430,13 @@ protected static Number stringToNumber(final String input) throws NumberFormatEx
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.");
throw new NumberFormatException("val ["+input+"] 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.");
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
}
// integer representation.
Expand All @@ -2450,7 +2456,7 @@ protected static Number stringToNumber(final String input) throws NumberFormatEx
}
return bi;
}
throw new NumberFormatException("val ["+val+"] is not a valid number.");
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}

/**
Expand Down Expand Up @@ -2486,8 +2492,7 @@ public static Object stringToValue(String string) {
* produced, then the value will just be a string.
*/

char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
if (potentialNumber(string)) {
try {
return stringToNumber(string);
} catch (Exception ignore) {
Expand All @@ -2496,6 +2501,28 @@ public static Object stringToValue(String string) {
return string;
}


private static boolean potentialNumber(String value){
if (value == null || value.isEmpty()){
return false;
}
return potentialPositiveNumberStartingAtIndex(value, (value.charAt(0)=='-'?1:0));
}

private static boolean potentialPositiveNumberStartingAtIndex(String value,int index){
if (index >= value.length()){
return false;
}
return digitAtIndex(value, (value.charAt(index)=='.'?index+1:index));
}

private static boolean digitAtIndex(String value, int index){
if (index >= value.length()){
return false;
}
return value.charAt(index) >= '0' && value.charAt(index) <= '9';
}

/**
* Throw an exception if the object is a NaN or infinite number.
*
Expand Down Expand Up @@ -2902,26 +2929,15 @@ private static JSONException recursivelyDefinedObjectException(String key) {
* @return number without leading zeros
*/
private static String removeLeadingZerosOfNumber(String value){
char[] chars = value.toCharArray();
int leftMostUnsignedIndex = 0;
if (chars[0] == '-'){
leftMostUnsignedIndex = 1;
}
int firstNonZeroCharIndex = -1;
for (int i=leftMostUnsignedIndex;i<value.length();i++){
if (chars[i] != '0'){
firstNonZeroCharIndex = i;
break;
if (value.equals("-")){return value;}
boolean negativeFirstChar = (value.charAt(0) == '-');
int counter = negativeFirstChar ? 1:0;
while (counter < value.length()){
if (value.charAt(counter) != '0'){
return String.format("%s%s", negativeFirstChar?'-':"",value.substring(counter));
}
++counter;
}
if (firstNonZeroCharIndex == -1){
return value;
}
StringBuilder result = new StringBuilder();
if (leftMostUnsignedIndex == 1){
result.append('-');
}
result.append(value.substring(firstNonZeroCharIndex));
return result.toString();
return String.format("%s%s", negativeFirstChar?'-':"",'0');
}
}
100 changes: 100 additions & 0 deletions src/test/java/org/json/junit/JSONObjectDecimalTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package org.json.junit;

import org.json.JSONObject;
import org.junit.Test;

import java.math.BigDecimal;
import java.math.BigInteger;

import static org.junit.Assert.assertEquals;

public class JSONObjectDecimalTest {

@Test
public void shouldParseDecimalNumberThatStartsWithDecimalPoint(){
JSONObject jsonObject = new JSONObject("{value:0.50}");
assertEquals("Float not recognized", 0.5f, jsonObject.getFloat("value"), 0.0f);
assertEquals("Float not recognized", 0.5f, jsonObject.optFloat("value"), 0.0f);
assertEquals("Float not recognized", 0.5f, jsonObject.optFloatObject("value"), 0.0f);
assertEquals("Double not recognized", 0.5d, jsonObject.optDouble("value"), 0.0f);
assertEquals("Double not recognized", 0.5d, jsonObject.optDoubleObject("value"), 0.0f);
assertEquals("Double not recognized", 0.5d, jsonObject.getDouble("value"), 0.0f);
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(.5).compareTo(jsonObject.getBigDecimal("value")));
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
}



@Test
public void shouldParseNegativeDecimalNumberThatStartsWithDecimalPoint(){
JSONObject jsonObject = new JSONObject("{value:-.50}");
assertEquals("Float not recognized", -0.5f, jsonObject.getFloat("value"), 0.0f);
assertEquals("Float not recognized", -0.5f, jsonObject.optFloat("value"), 0.0f);
assertEquals("Float not recognized", -0.5f, jsonObject.optFloatObject("value"), 0.0f);
assertEquals("Double not recognized", -0.5d, jsonObject.optDouble("value"), 0.0f);
assertEquals("Double not recognized", -0.5d, jsonObject.optDoubleObject("value"), 0.0f);
assertEquals("Double not recognized", -0.5d, jsonObject.getDouble("value"), 0.0f);
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(-.5).compareTo(jsonObject.getBigDecimal("value")));
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
}

@Test
public void shouldParseDecimalNumberThatHasZeroBeforeWithDecimalPoint(){
JSONObject jsonObject = new JSONObject("{value:00.050}");
assertEquals("Float not recognized", 0.05f, jsonObject.getFloat("value"), 0.0f);
assertEquals("Float not recognized", 0.05f, jsonObject.optFloat("value"), 0.0f);
assertEquals("Float not recognized", 0.05f, jsonObject.optFloatObject("value"), 0.0f);
assertEquals("Double not recognized", 0.05d, jsonObject.optDouble("value"), 0.0f);
assertEquals("Double not recognized", 0.05d, jsonObject.optDoubleObject("value"), 0.0f);
assertEquals("Double not recognized", 0.05d, jsonObject.getDouble("value"), 0.0f);
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(.05).compareTo(jsonObject.getBigDecimal("value")));
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
}

@Test
public void shouldParseNegativeDecimalNumberThatHasZeroBeforeWithDecimalPoint(){
JSONObject jsonObject = new JSONObject("{value:-00.050}");
assertEquals("Float not recognized", -0.05f, jsonObject.getFloat("value"), 0.0f);
assertEquals("Float not recognized", -0.05f, jsonObject.optFloat("value"), 0.0f);
assertEquals("Float not recognized", -0.05f, jsonObject.optFloatObject("value"), 0.0f);
assertEquals("Double not recognized", -0.05d, jsonObject.optDouble("value"), 0.0f);
assertEquals("Double not recognized", -0.05d, jsonObject.optDoubleObject("value"), 0.0f);
assertEquals("Double not recognized", -0.05d, jsonObject.getDouble("value"), 0.0f);
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(-.05).compareTo(jsonObject.getBigDecimal("value")));
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
}


}
49 changes: 40 additions & 9 deletions src/test/java/org/json/junit/JSONObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Public Domain.
*/

import static java.lang.Double.NaN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
Expand Down Expand Up @@ -782,7 +783,7 @@ public void jsonObjectAccumulate() {
jsonObject.accumulate("myArray", -23.45e7);
// include an unsupported object for coverage
try {
jsonObject.accumulate("myArray", Double.NaN);
jsonObject.accumulate("myArray", NaN);
fail("Expected exception");
} catch (JSONException ignored) {}

Expand Down Expand Up @@ -814,7 +815,7 @@ public void jsonObjectAppend() {
jsonObject.append("myArray", -23.45e7);
// include an unsupported object for coverage
try {
jsonObject.append("myArray", Double.NaN);
jsonObject.append("myArray", NaN);
fail("Expected exception");
} catch (JSONException ignored) {}

Expand All @@ -839,7 +840,7 @@ public void jsonObjectAppend() {
public void jsonObjectDoubleToString() {
String [] expectedStrs = {"1", "1", "-23.4", "-2.345E68", "null", "null" };
Double [] doubles = { 1.0, 00001.00000, -23.4, -23.45e67,
Double.NaN, Double.NEGATIVE_INFINITY };
NaN, Double.NEGATIVE_INFINITY };
for (int i = 0; i < expectedStrs.length; ++i) {
String actualStr = JSONObject.doubleToString(doubles[i]);
assertTrue("value expected ["+expectedStrs[i]+
Expand Down Expand Up @@ -894,11 +895,11 @@ public void jsonObjectValues() {
assertTrue("opt doubleKey should be double",
jsonObject.optDouble("doubleKey") == -23.45e7);
assertTrue("opt doubleKey with Default should be double",
jsonObject.optDouble("doubleStrKey", Double.NaN) == 1);
jsonObject.optDouble("doubleStrKey", NaN) == 1);
assertTrue("opt doubleKey should be Double",
Double.valueOf(-23.45e7).equals(jsonObject.optDoubleObject("doubleKey")));
assertTrue("opt doubleKey with Default should be Double",
Double.valueOf(1).equals(jsonObject.optDoubleObject("doubleStrKey", Double.NaN)));
Double.valueOf(1).equals(jsonObject.optDoubleObject("doubleStrKey", NaN)));
assertTrue("opt negZeroKey should be a Double",
jsonObject.opt("negZeroKey") instanceof Double);
assertTrue("get negZeroKey should be a Double",
Expand Down Expand Up @@ -1071,9 +1072,14 @@ public void jsonInvalidNumberValues() {
"\"hexFloat\":0x1.0P-1074,"+
"\"floatIdentifier\":0.1f,"+
"\"doubleIdentifier\":0.1d,"+
"\"doubleIdentifierWithMultipleLeadingZerosBeforeDecimal\":0000000.1d,"+
"\"negativeDoubleIdentifierWithMultipleLeadingZerosBeforeDecimal\":-0000000.1d,"+
"\"doubleIdentifierWithMultipleLeadingZerosAfterDecimal\":0000000.0001d,"+
"\"negativeDoubleIdentifierWithMultipleLeadingZerosAfterDecimal\":-0000000.0001d,"+
"\"integerWithLeadingZeros\":000900,"+
"\"integerWithAllZeros\":00000,"+
"\"compositeWithLeadingZeros\":00800.90d"+
"\"compositeWithLeadingZeros\":00800.90d,"+
"\"decimalPositiveWithoutNumberBeforeDecimalPoint\":.90,"+
"}";
JSONObject jsonObject = new JSONObject(str);
Object obj;
Expand All @@ -1083,7 +1089,7 @@ public void jsonInvalidNumberValues() {
assertTrue("hexNumber currently evaluates to string",
obj.equals("-0x123"));
assertTrue( "tooManyZeros currently evaluates to string",
jsonObject.get( "tooManyZeros" ).equals("00"));
jsonObject.get( "tooManyZeros" ).equals(0));
obj = jsonObject.get("negativeInfinite");
assertTrue( "negativeInfinite currently evaluates to string",
obj.equals("-Infinity"));
Expand All @@ -1109,14 +1115,24 @@ public void jsonInvalidNumberValues() {
jsonObject.get("floatIdentifier").equals(Double.valueOf(0.1)));
assertTrue("doubleIdentifier currently evaluates to double 0.1",
jsonObject.get("doubleIdentifier").equals(Double.valueOf(0.1)));
assertTrue("doubleIdentifierWithMultipleLeadingZerosBeforeDecimal currently evaluates to double 0.1",
jsonObject.get("doubleIdentifierWithMultipleLeadingZerosBeforeDecimal").equals(Double.valueOf(0.1)));
assertTrue("negativeDoubleIdentifierWithMultipleLeadingZerosBeforeDecimal currently evaluates to double -0.1",
jsonObject.get("negativeDoubleIdentifierWithMultipleLeadingZerosBeforeDecimal").equals(Double.valueOf(-0.1)));
assertTrue("doubleIdentifierWithMultipleLeadingZerosAfterDecimal currently evaluates to double 0.0001",
jsonObject.get("doubleIdentifierWithMultipleLeadingZerosAfterDecimal").equals(Double.valueOf(0.0001)));
assertTrue("doubleIdentifierWithMultipleLeadingZerosAfterDecimal currently evaluates to double 0.0001",
jsonObject.get("doubleIdentifierWithMultipleLeadingZerosAfterDecimal").equals(Double.valueOf(0.0001)));
assertTrue("negativeDoubleIdentifierWithMultipleLeadingZerosAfterDecimal currently evaluates to double -0.0001",
jsonObject.get("negativeDoubleIdentifierWithMultipleLeadingZerosAfterDecimal").equals(Double.valueOf(-0.0001)));
assertTrue("Integer does not evaluate to 900",
jsonObject.get("integerWithLeadingZeros").equals(900));
assertTrue("Integer does not evaluate to 900",
jsonObject.getInt("integerWithLeadingZeros")==900);
assertTrue("Integer does not evaluate to 900",
jsonObject.optInt("integerWithLeadingZeros")==900);
assertTrue("Integer does not evaluate to 0",
jsonObject.get("integerWithAllZeros").equals("00000"));
jsonObject.get("integerWithAllZeros").equals(0));
assertTrue("Integer does not evaluate to 0",
jsonObject.getInt("integerWithAllZeros")==0);
assertTrue("Integer does not evaluate to 0",
Expand All @@ -1131,6 +1147,21 @@ public void jsonInvalidNumberValues() {
jsonObject.getLong("compositeWithLeadingZeros")==800);
assertTrue("Long does not evaluate to 800.90",
jsonObject.optLong("compositeWithLeadingZeros")==800);
assertEquals("Get long of decimalPositiveWithoutNumberBeforeDecimalPoint does not match",
0.9d,jsonObject.getDouble("decimalPositiveWithoutNumberBeforeDecimalPoint"), 0.0d);
assertEquals("Get long of decimalPositiveWithoutNumberBeforeDecimalPoint does not match",
0.9d,jsonObject.optDouble("decimalPositiveWithoutNumberBeforeDecimalPoint"), 0.0d);
assertEquals("Get long of decimalPositiveWithoutNumberBeforeDecimalPoint does not match",
0.0d,jsonObject.optLong("decimalPositiveWithoutNumberBeforeDecimalPoint"), 0.0d);

assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
0.0001d,jsonObject.getDouble("doubleIdentifierWithMultipleLeadingZerosAfterDecimal"), 0.0d);
assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
0.0001d,jsonObject.optDouble("doubleIdentifierWithMultipleLeadingZerosAfterDecimal"), 0.0d);
assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
0.0d, jsonObject.getLong("doubleIdentifierWithMultipleLeadingZerosAfterDecimal") , 0.0d);
assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
0.0d,jsonObject.optLong("doubleIdentifierWithMultipleLeadingZerosAfterDecimal"), 0.0d);
Util.checkJSONObjectMaps(jsonObject);
}

Expand Down Expand Up @@ -2361,7 +2392,7 @@ public void jsonObjectParsingErrors() {
}
try {
// test validity of invalid double
JSONObject.testValidity(Double.NaN);
JSONObject.testValidity(NaN);
fail("Expected an exception");
} catch (JSONException e) {
assertTrue("", true);
Expand Down
Loading

0 comments on commit 0cdc38a

Please sign in to comment.