Skip to content

Commit 0cdc38a

Browse files
committed
#653 - review comments updated.
1 parent d5277b1 commit 0cdc38a

File tree

4 files changed

+236
-34
lines changed

4 files changed

+236
-34
lines changed

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

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,8 +2392,14 @@ protected static boolean isDecimalNotation(final String val) {
23922392
*/
23932393
protected static Number stringToNumber(final String input) throws NumberFormatException {
23942394
String val = input;
2395+
if (val.startsWith(".")){
2396+
val = "0"+val;
2397+
}
2398+
if (val.startsWith("-.")){
2399+
val = "-0."+val.substring(2);
2400+
}
23952401
char initial = val.charAt(0);
2396-
if ((initial >= '0' && initial <= '9') || initial == '-') {
2402+
if ((initial >= '0' && initial <= '9') || initial == '-' ) {
23972403
// decimal representation
23982404
if (isDecimalNotation(val)) {
23992405
// Use a BigDecimal all the time so we keep the original
@@ -2424,13 +2430,13 @@ protected static Number stringToNumber(final String input) throws NumberFormatEx
24242430
if(initial == '0' && val.length() > 1) {
24252431
char at1 = val.charAt(1);
24262432
if(at1 >= '0' && at1 <= '9') {
2427-
throw new NumberFormatException("val ["+val+"] is not a valid number.");
2433+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
24282434
}
24292435
} else if (initial == '-' && val.length() > 2) {
24302436
char at1 = val.charAt(1);
24312437
char at2 = val.charAt(2);
24322438
if(at1 == '0' && at2 >= '0' && at2 <= '9') {
2433-
throw new NumberFormatException("val ["+val+"] is not a valid number.");
2439+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
24342440
}
24352441
}
24362442
// integer representation.
@@ -2450,7 +2456,7 @@ protected static Number stringToNumber(final String input) throws NumberFormatEx
24502456
}
24512457
return bi;
24522458
}
2453-
throw new NumberFormatException("val ["+val+"] is not a valid number.");
2459+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
24542460
}
24552461

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

2489-
char initial = string.charAt(0);
2490-
if ((initial >= '0' && initial <= '9') || initial == '-') {
2495+
if (potentialNumber(string)) {
24912496
try {
24922497
return stringToNumber(string);
24932498
} catch (Exception ignore) {
@@ -2496,6 +2501,28 @@ public static Object stringToValue(String string) {
24962501
return string;
24972502
}
24982503

2504+
2505+
private static boolean potentialNumber(String value){
2506+
if (value == null || value.isEmpty()){
2507+
return false;
2508+
}
2509+
return potentialPositiveNumberStartingAtIndex(value, (value.charAt(0)=='-'?1:0));
2510+
}
2511+
2512+
private static boolean potentialPositiveNumberStartingAtIndex(String value,int index){
2513+
if (index >= value.length()){
2514+
return false;
2515+
}
2516+
return digitAtIndex(value, (value.charAt(index)=='.'?index+1:index));
2517+
}
2518+
2519+
private static boolean digitAtIndex(String value, int index){
2520+
if (index >= value.length()){
2521+
return false;
2522+
}
2523+
return value.charAt(index) >= '0' && value.charAt(index) <= '9';
2524+
}
2525+
24992526
/**
25002527
* Throw an exception if the object is a NaN or infinite number.
25012528
*
@@ -2902,26 +2929,15 @@ private static JSONException recursivelyDefinedObjectException(String key) {
29022929
* @return number without leading zeros
29032930
*/
29042931
private static String removeLeadingZerosOfNumber(String value){
2905-
char[] chars = value.toCharArray();
2906-
int leftMostUnsignedIndex = 0;
2907-
if (chars[0] == '-'){
2908-
leftMostUnsignedIndex = 1;
2909-
}
2910-
int firstNonZeroCharIndex = -1;
2911-
for (int i=leftMostUnsignedIndex;i<value.length();i++){
2912-
if (chars[i] != '0'){
2913-
firstNonZeroCharIndex = i;
2914-
break;
2932+
if (value.equals("-")){return value;}
2933+
boolean negativeFirstChar = (value.charAt(0) == '-');
2934+
int counter = negativeFirstChar ? 1:0;
2935+
while (counter < value.length()){
2936+
if (value.charAt(counter) != '0'){
2937+
return String.format("%s%s", negativeFirstChar?'-':"",value.substring(counter));
29152938
}
2939+
++counter;
29162940
}
2917-
if (firstNonZeroCharIndex == -1){
2918-
return value;
2919-
}
2920-
StringBuilder result = new StringBuilder();
2921-
if (leftMostUnsignedIndex == 1){
2922-
result.append('-');
2923-
}
2924-
result.append(value.substring(firstNonZeroCharIndex));
2925-
return result.toString();
2941+
return String.format("%s%s", negativeFirstChar?'-':"",'0');
29262942
}
29272943
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package org.json.junit;
2+
3+
import org.json.JSONObject;
4+
import org.junit.Test;
5+
6+
import java.math.BigDecimal;
7+
import java.math.BigInteger;
8+
9+
import static org.junit.Assert.assertEquals;
10+
11+
public class JSONObjectDecimalTest {
12+
13+
@Test
14+
public void shouldParseDecimalNumberThatStartsWithDecimalPoint(){
15+
JSONObject jsonObject = new JSONObject("{value:0.50}");
16+
assertEquals("Float not recognized", 0.5f, jsonObject.getFloat("value"), 0.0f);
17+
assertEquals("Float not recognized", 0.5f, jsonObject.optFloat("value"), 0.0f);
18+
assertEquals("Float not recognized", 0.5f, jsonObject.optFloatObject("value"), 0.0f);
19+
assertEquals("Double not recognized", 0.5d, jsonObject.optDouble("value"), 0.0f);
20+
assertEquals("Double not recognized", 0.5d, jsonObject.optDoubleObject("value"), 0.0f);
21+
assertEquals("Double not recognized", 0.5d, jsonObject.getDouble("value"), 0.0f);
22+
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
23+
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
24+
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
25+
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
26+
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
27+
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
28+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
29+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
30+
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(.5).compareTo(jsonObject.getBigDecimal("value")));
31+
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
32+
}
33+
34+
35+
36+
@Test
37+
public void shouldParseNegativeDecimalNumberThatStartsWithDecimalPoint(){
38+
JSONObject jsonObject = new JSONObject("{value:-.50}");
39+
assertEquals("Float not recognized", -0.5f, jsonObject.getFloat("value"), 0.0f);
40+
assertEquals("Float not recognized", -0.5f, jsonObject.optFloat("value"), 0.0f);
41+
assertEquals("Float not recognized", -0.5f, jsonObject.optFloatObject("value"), 0.0f);
42+
assertEquals("Double not recognized", -0.5d, jsonObject.optDouble("value"), 0.0f);
43+
assertEquals("Double not recognized", -0.5d, jsonObject.optDoubleObject("value"), 0.0f);
44+
assertEquals("Double not recognized", -0.5d, jsonObject.getDouble("value"), 0.0f);
45+
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
46+
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
47+
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
48+
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
49+
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
50+
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
51+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
52+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
53+
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(-.5).compareTo(jsonObject.getBigDecimal("value")));
54+
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
55+
}
56+
57+
@Test
58+
public void shouldParseDecimalNumberThatHasZeroBeforeWithDecimalPoint(){
59+
JSONObject jsonObject = new JSONObject("{value:00.050}");
60+
assertEquals("Float not recognized", 0.05f, jsonObject.getFloat("value"), 0.0f);
61+
assertEquals("Float not recognized", 0.05f, jsonObject.optFloat("value"), 0.0f);
62+
assertEquals("Float not recognized", 0.05f, jsonObject.optFloatObject("value"), 0.0f);
63+
assertEquals("Double not recognized", 0.05d, jsonObject.optDouble("value"), 0.0f);
64+
assertEquals("Double not recognized", 0.05d, jsonObject.optDoubleObject("value"), 0.0f);
65+
assertEquals("Double not recognized", 0.05d, jsonObject.getDouble("value"), 0.0f);
66+
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
67+
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
68+
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
69+
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
70+
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
71+
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
72+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
73+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
74+
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(.05).compareTo(jsonObject.getBigDecimal("value")));
75+
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
76+
}
77+
78+
@Test
79+
public void shouldParseNegativeDecimalNumberThatHasZeroBeforeWithDecimalPoint(){
80+
JSONObject jsonObject = new JSONObject("{value:-00.050}");
81+
assertEquals("Float not recognized", -0.05f, jsonObject.getFloat("value"), 0.0f);
82+
assertEquals("Float not recognized", -0.05f, jsonObject.optFloat("value"), 0.0f);
83+
assertEquals("Float not recognized", -0.05f, jsonObject.optFloatObject("value"), 0.0f);
84+
assertEquals("Double not recognized", -0.05d, jsonObject.optDouble("value"), 0.0f);
85+
assertEquals("Double not recognized", -0.05d, jsonObject.optDoubleObject("value"), 0.0f);
86+
assertEquals("Double not recognized", -0.05d, jsonObject.getDouble("value"), 0.0f);
87+
assertEquals("Long not recognized", 0, jsonObject.optLong("value"), 0);
88+
assertEquals("Long not recognized", 0, jsonObject.getLong("value"), 0);
89+
assertEquals("Long not recognized", 0, jsonObject.optLongObject("value"), 0);
90+
assertEquals("Integer not recognized", 0, jsonObject.optInt("value"), 0);
91+
assertEquals("Integer not recognized", 0, jsonObject.getInt("value"), 0);
92+
assertEquals("Integer not recognized", 0, jsonObject.optIntegerObject("value"), 0);
93+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").intValue(), 0);
94+
assertEquals("Number not recognized", 0, jsonObject.getNumber("value").longValue(), 0);
95+
assertEquals("BigDecimal not recognized", 0, BigDecimal.valueOf(-.05).compareTo(jsonObject.getBigDecimal("value")));
96+
assertEquals("BigInteger not recognized",0, BigInteger.valueOf(0).compareTo(jsonObject.getBigInteger("value")));
97+
}
98+
99+
100+
}

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

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Public Domain.
55
*/
66

7+
import static java.lang.Double.NaN;
78
import static org.junit.Assert.assertEquals;
89
import static org.junit.Assert.assertFalse;
910
import static org.junit.Assert.assertNotEquals;
@@ -782,7 +783,7 @@ public void jsonObjectAccumulate() {
782783
jsonObject.accumulate("myArray", -23.45e7);
783784
// include an unsupported object for coverage
784785
try {
785-
jsonObject.accumulate("myArray", Double.NaN);
786+
jsonObject.accumulate("myArray", NaN);
786787
fail("Expected exception");
787788
} catch (JSONException ignored) {}
788789

@@ -814,7 +815,7 @@ public void jsonObjectAppend() {
814815
jsonObject.append("myArray", -23.45e7);
815816
// include an unsupported object for coverage
816817
try {
817-
jsonObject.append("myArray", Double.NaN);
818+
jsonObject.append("myArray", NaN);
818819
fail("Expected exception");
819820
} catch (JSONException ignored) {}
820821

@@ -839,7 +840,7 @@ public void jsonObjectAppend() {
839840
public void jsonObjectDoubleToString() {
840841
String [] expectedStrs = {"1", "1", "-23.4", "-2.345E68", "null", "null" };
841842
Double [] doubles = { 1.0, 00001.00000, -23.4, -23.45e67,
842-
Double.NaN, Double.NEGATIVE_INFINITY };
843+
NaN, Double.NEGATIVE_INFINITY };
843844
for (int i = 0; i < expectedStrs.length; ++i) {
844845
String actualStr = JSONObject.doubleToString(doubles[i]);
845846
assertTrue("value expected ["+expectedStrs[i]+
@@ -894,11 +895,11 @@ public void jsonObjectValues() {
894895
assertTrue("opt doubleKey should be double",
895896
jsonObject.optDouble("doubleKey") == -23.45e7);
896897
assertTrue("opt doubleKey with Default should be double",
897-
jsonObject.optDouble("doubleStrKey", Double.NaN) == 1);
898+
jsonObject.optDouble("doubleStrKey", NaN) == 1);
898899
assertTrue("opt doubleKey should be Double",
899900
Double.valueOf(-23.45e7).equals(jsonObject.optDoubleObject("doubleKey")));
900901
assertTrue("opt doubleKey with Default should be Double",
901-
Double.valueOf(1).equals(jsonObject.optDoubleObject("doubleStrKey", Double.NaN)));
902+
Double.valueOf(1).equals(jsonObject.optDoubleObject("doubleStrKey", NaN)));
902903
assertTrue("opt negZeroKey should be a Double",
903904
jsonObject.opt("negZeroKey") instanceof Double);
904905
assertTrue("get negZeroKey should be a Double",
@@ -1071,9 +1072,14 @@ public void jsonInvalidNumberValues() {
10711072
"\"hexFloat\":0x1.0P-1074,"+
10721073
"\"floatIdentifier\":0.1f,"+
10731074
"\"doubleIdentifier\":0.1d,"+
1075+
"\"doubleIdentifierWithMultipleLeadingZerosBeforeDecimal\":0000000.1d,"+
1076+
"\"negativeDoubleIdentifierWithMultipleLeadingZerosBeforeDecimal\":-0000000.1d,"+
1077+
"\"doubleIdentifierWithMultipleLeadingZerosAfterDecimal\":0000000.0001d,"+
1078+
"\"negativeDoubleIdentifierWithMultipleLeadingZerosAfterDecimal\":-0000000.0001d,"+
10741079
"\"integerWithLeadingZeros\":000900,"+
10751080
"\"integerWithAllZeros\":00000,"+
1076-
"\"compositeWithLeadingZeros\":00800.90d"+
1081+
"\"compositeWithLeadingZeros\":00800.90d,"+
1082+
"\"decimalPositiveWithoutNumberBeforeDecimalPoint\":.90,"+
10771083
"}";
10781084
JSONObject jsonObject = new JSONObject(str);
10791085
Object obj;
@@ -1083,7 +1089,7 @@ public void jsonInvalidNumberValues() {
10831089
assertTrue("hexNumber currently evaluates to string",
10841090
obj.equals("-0x123"));
10851091
assertTrue( "tooManyZeros currently evaluates to string",
1086-
jsonObject.get( "tooManyZeros" ).equals("00"));
1092+
jsonObject.get( "tooManyZeros" ).equals(0));
10871093
obj = jsonObject.get("negativeInfinite");
10881094
assertTrue( "negativeInfinite currently evaluates to string",
10891095
obj.equals("-Infinity"));
@@ -1109,14 +1115,24 @@ public void jsonInvalidNumberValues() {
11091115
jsonObject.get("floatIdentifier").equals(Double.valueOf(0.1)));
11101116
assertTrue("doubleIdentifier currently evaluates to double 0.1",
11111117
jsonObject.get("doubleIdentifier").equals(Double.valueOf(0.1)));
1118+
assertTrue("doubleIdentifierWithMultipleLeadingZerosBeforeDecimal currently evaluates to double 0.1",
1119+
jsonObject.get("doubleIdentifierWithMultipleLeadingZerosBeforeDecimal").equals(Double.valueOf(0.1)));
1120+
assertTrue("negativeDoubleIdentifierWithMultipleLeadingZerosBeforeDecimal currently evaluates to double -0.1",
1121+
jsonObject.get("negativeDoubleIdentifierWithMultipleLeadingZerosBeforeDecimal").equals(Double.valueOf(-0.1)));
1122+
assertTrue("doubleIdentifierWithMultipleLeadingZerosAfterDecimal currently evaluates to double 0.0001",
1123+
jsonObject.get("doubleIdentifierWithMultipleLeadingZerosAfterDecimal").equals(Double.valueOf(0.0001)));
1124+
assertTrue("doubleIdentifierWithMultipleLeadingZerosAfterDecimal currently evaluates to double 0.0001",
1125+
jsonObject.get("doubleIdentifierWithMultipleLeadingZerosAfterDecimal").equals(Double.valueOf(0.0001)));
1126+
assertTrue("negativeDoubleIdentifierWithMultipleLeadingZerosAfterDecimal currently evaluates to double -0.0001",
1127+
jsonObject.get("negativeDoubleIdentifierWithMultipleLeadingZerosAfterDecimal").equals(Double.valueOf(-0.0001)));
11121128
assertTrue("Integer does not evaluate to 900",
11131129
jsonObject.get("integerWithLeadingZeros").equals(900));
11141130
assertTrue("Integer does not evaluate to 900",
11151131
jsonObject.getInt("integerWithLeadingZeros")==900);
11161132
assertTrue("Integer does not evaluate to 900",
11171133
jsonObject.optInt("integerWithLeadingZeros")==900);
11181134
assertTrue("Integer does not evaluate to 0",
1119-
jsonObject.get("integerWithAllZeros").equals("00000"));
1135+
jsonObject.get("integerWithAllZeros").equals(0));
11201136
assertTrue("Integer does not evaluate to 0",
11211137
jsonObject.getInt("integerWithAllZeros")==0);
11221138
assertTrue("Integer does not evaluate to 0",
@@ -1131,6 +1147,21 @@ public void jsonInvalidNumberValues() {
11311147
jsonObject.getLong("compositeWithLeadingZeros")==800);
11321148
assertTrue("Long does not evaluate to 800.90",
11331149
jsonObject.optLong("compositeWithLeadingZeros")==800);
1150+
assertEquals("Get long of decimalPositiveWithoutNumberBeforeDecimalPoint does not match",
1151+
0.9d,jsonObject.getDouble("decimalPositiveWithoutNumberBeforeDecimalPoint"), 0.0d);
1152+
assertEquals("Get long of decimalPositiveWithoutNumberBeforeDecimalPoint does not match",
1153+
0.9d,jsonObject.optDouble("decimalPositiveWithoutNumberBeforeDecimalPoint"), 0.0d);
1154+
assertEquals("Get long of decimalPositiveWithoutNumberBeforeDecimalPoint does not match",
1155+
0.0d,jsonObject.optLong("decimalPositiveWithoutNumberBeforeDecimalPoint"), 0.0d);
1156+
1157+
assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
1158+
0.0001d,jsonObject.getDouble("doubleIdentifierWithMultipleLeadingZerosAfterDecimal"), 0.0d);
1159+
assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
1160+
0.0001d,jsonObject.optDouble("doubleIdentifierWithMultipleLeadingZerosAfterDecimal"), 0.0d);
1161+
assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
1162+
0.0d, jsonObject.getLong("doubleIdentifierWithMultipleLeadingZerosAfterDecimal") , 0.0d);
1163+
assertEquals("Get long of doubleIdentifierWithMultipleLeadingZerosAfterDecimal does not match",
1164+
0.0d,jsonObject.optLong("doubleIdentifierWithMultipleLeadingZerosAfterDecimal"), 0.0d);
11341165
Util.checkJSONObjectMaps(jsonObject);
11351166
}
11361167

@@ -2361,7 +2392,7 @@ public void jsonObjectParsingErrors() {
23612392
}
23622393
try {
23632394
// test validity of invalid double
2364-
JSONObject.testValidity(Double.NaN);
2395+
JSONObject.testValidity(NaN);
23652396
fail("Expected an exception");
23662397
} catch (JSONException e) {
23672398
assertTrue("", true);

0 commit comments

Comments
 (0)