Skip to content

Commit 1d0775c

Browse files
committed
Revert changes with feature and refactor together.
1 parent 7b2677a commit 1d0775c

8 files changed

Lines changed: 177 additions & 323 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ public Number getNumber(int index) throws JSONException {
331331
if (object instanceof Number) {
332332
return (Number)object;
333333
}
334-
return NumberConversionUtil.stringToNumber(object.toString());
334+
return JSONObject.stringToNumber(object.toString());
335335
} catch (Exception e) {
336336
throw wrongValueFormatException(index, "number", object, e);
337337
}
@@ -1078,7 +1078,7 @@ public Number optNumber(int index, Number defaultValue) {
10781078

10791079
if (val instanceof String) {
10801080
try {
1081-
return NumberConversionUtil.stringToNumber((String) val);
1081+
return JSONObject.stringToNumber((String) val);
10821082
} catch (Exception e) {
10831083
return defaultValue;
10841084
}

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

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
import java.util.Set;
2929
import java.util.regex.Pattern;
3030

31-
import static org.json.NumberConversionUtil.stringToNumber;
32-
3331
/**
3432
* A JSONObject is an unordered collection of name/value pairs. Its external
3533
* form is a string wrapped in curly braces with colons between the names and
@@ -2382,7 +2380,83 @@ protected static boolean isDecimalNotation(final String val) {
23822380
|| val.indexOf('E') > -1 || "-0".equals(val);
23832381
}
23842382

2385-
2383+
/**
2384+
* Converts a string to a number using the narrowest possible type. Possible
2385+
* returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
2386+
* When a Double is returned, it should always be a valid Double and not NaN or +-infinity.
2387+
*
2388+
* @param input value to convert
2389+
* @return Number representation of the value.
2390+
* @throws NumberFormatException thrown if the value is not a valid number. A public
2391+
* caller should catch this and wrap it in a {@link JSONException} if applicable.
2392+
*/
2393+
protected static Number stringToNumber(final String input) throws NumberFormatException {
2394+
String val = input;
2395+
if (val.startsWith(".")){
2396+
val = "0"+val;
2397+
}
2398+
if (val.startsWith("-.")){
2399+
val = "-0."+val.substring(2);
2400+
}
2401+
char initial = val.charAt(0);
2402+
if ((initial >= '0' && initial <= '9') || initial == '-' ) {
2403+
// decimal representation
2404+
if (isDecimalNotation(val)) {
2405+
// Use a BigDecimal all the time so we keep the original
2406+
// representation. BigDecimal doesn't support -0.0, ensure we
2407+
// keep that by forcing a decimal.
2408+
try {
2409+
BigDecimal bd = new BigDecimal(val);
2410+
if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) {
2411+
return Double.valueOf(-0.0);
2412+
}
2413+
return bd;
2414+
} catch (NumberFormatException retryAsDouble) {
2415+
// this is to support "Hex Floats" like this: 0x1.0P-1074
2416+
try {
2417+
Double d = Double.valueOf(val);
2418+
if(d.isNaN() || d.isInfinite()) {
2419+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
2420+
}
2421+
return d;
2422+
} catch (NumberFormatException ignore) {
2423+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
2424+
}
2425+
}
2426+
}
2427+
val = removeLeadingZerosOfNumber(input);
2428+
initial = val.charAt(0);
2429+
if(initial == '0' && val.length() > 1) {
2430+
char at1 = val.charAt(1);
2431+
if(at1 >= '0' && at1 <= '9') {
2432+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
2433+
}
2434+
} else if (initial == '-' && val.length() > 2) {
2435+
char at1 = val.charAt(1);
2436+
char at2 = val.charAt(2);
2437+
if(at1 == '0' && at2 >= '0' && at2 <= '9') {
2438+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
2439+
}
2440+
}
2441+
// integer representation.
2442+
// This will narrow any values to the smallest reasonable Object representation
2443+
// (Integer, Long, or BigInteger)
2444+
2445+
// BigInteger down conversion: We use a similar bitLength compare as
2446+
// BigInteger#intValueExact uses. Increases GC, but objects hold
2447+
// only what they need. i.e. Less runtime overhead if the value is
2448+
// long lived.
2449+
BigInteger bi = new BigInteger(val);
2450+
if(bi.bitLength() <= 31){
2451+
return Integer.valueOf(bi.intValue());
2452+
}
2453+
if(bi.bitLength() <= 63){
2454+
return Long.valueOf(bi.longValue());
2455+
}
2456+
return bi;
2457+
}
2458+
throw new NumberFormatException("val ["+input+"] is not a valid number.");
2459+
}
23862460

23872461
/**
23882462
* Try to convert a string into a number, boolean, or null. If the string
@@ -2848,4 +2922,23 @@ private static JSONException recursivelyDefinedObjectException(String key) {
28482922
);
28492923
}
28502924

2925+
/**
2926+
* For a prospective number, remove the leading zeros
2927+
* @param value prospective number
2928+
* @return number without leading zeros
2929+
*/
2930+
private static String removeLeadingZerosOfNumber(String value){
2931+
if (value.equals("-")){return value;}
2932+
boolean negativeFirstChar = (value.charAt(0) == '-');
2933+
int counter = negativeFirstChar ? 1:0;
2934+
while (counter < value.length()){
2935+
if (value.charAt(counter) != '0'){
2936+
if (negativeFirstChar) {return "-".concat(value.substring(counter));}
2937+
return value.substring(counter);
2938+
}
2939+
++counter;
2940+
}
2941+
if (negativeFirstChar) {return "-0";}
2942+
return "0";
2943+
}
28512944
}

src/main/java/org/json/NumberConversionUtil.java

Lines changed: 0 additions & 142 deletions
This file was deleted.

src/main/java/org/json/XML.java

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66

77
import java.io.Reader;
88
import java.io.StringReader;
9+
import java.math.BigDecimal;
10+
import java.math.BigInteger;
911
import java.util.Iterator;
1012

11-
import static org.json.NumberConversionUtil.potentialNumber;
12-
import static org.json.NumberConversionUtil.stringToNumber;
13-
1413

1514
/**
1615
* This provides static methods to convert an XML text into a JSONObject, and to
@@ -487,7 +486,8 @@ public static Object stringToValue(String string) {
487486
* produced, then the value will just be a string.
488487
*/
489488

490-
if (potentialNumber(string)) {
489+
char initial = string.charAt(0);
490+
if ((initial >= '0' && initial <= '9') || initial == '-') {
491491
try {
492492
return stringToNumber(string);
493493
} catch (Exception ignore) {
@@ -496,6 +496,78 @@ public static Object stringToValue(String string) {
496496
return string;
497497
}
498498

499+
/**
500+
* direct copy of {@link JSONObject#stringToNumber(String)} to maintain Android support.
501+
*/
502+
private static Number stringToNumber(final String val) throws NumberFormatException {
503+
char initial = val.charAt(0);
504+
if ((initial >= '0' && initial <= '9') || initial == '-') {
505+
// decimal representation
506+
if (isDecimalNotation(val)) {
507+
// Use a BigDecimal all the time so we keep the original
508+
// representation. BigDecimal doesn't support -0.0, ensure we
509+
// keep that by forcing a decimal.
510+
try {
511+
BigDecimal bd = new BigDecimal(val);
512+
if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) {
513+
return Double.valueOf(-0.0);
514+
}
515+
return bd;
516+
} catch (NumberFormatException retryAsDouble) {
517+
// this is to support "Hex Floats" like this: 0x1.0P-1074
518+
try {
519+
Double d = Double.valueOf(val);
520+
if(d.isNaN() || d.isInfinite()) {
521+
throw new NumberFormatException("val ["+val+"] is not a valid number.");
522+
}
523+
return d;
524+
} catch (NumberFormatException ignore) {
525+
throw new NumberFormatException("val ["+val+"] is not a valid number.");
526+
}
527+
}
528+
}
529+
// block items like 00 01 etc. Java number parsers treat these as Octal.
530+
if(initial == '0' && val.length() > 1) {
531+
char at1 = val.charAt(1);
532+
if(at1 >= '0' && at1 <= '9') {
533+
throw new NumberFormatException("val ["+val+"] is not a valid number.");
534+
}
535+
} else if (initial == '-' && val.length() > 2) {
536+
char at1 = val.charAt(1);
537+
char at2 = val.charAt(2);
538+
if(at1 == '0' && at2 >= '0' && at2 <= '9') {
539+
throw new NumberFormatException("val ["+val+"] is not a valid number.");
540+
}
541+
}
542+
// integer representation.
543+
// This will narrow any values to the smallest reasonable Object representation
544+
// (Integer, Long, or BigInteger)
545+
546+
// BigInteger down conversion: We use a similar bitLength compare as
547+
// BigInteger#intValueExact uses. Increases GC, but objects hold
548+
// only what they need. i.e. Less runtime overhead if the value is
549+
// long lived.
550+
BigInteger bi = new BigInteger(val);
551+
if(bi.bitLength() <= 31){
552+
return Integer.valueOf(bi.intValue());
553+
}
554+
if(bi.bitLength() <= 63){
555+
return Long.valueOf(bi.longValue());
556+
}
557+
return bi;
558+
}
559+
throw new NumberFormatException("val ["+val+"] is not a valid number.");
560+
}
561+
562+
/**
563+
* direct copy of {@link JSONObject#isDecimalNotation(String)} to maintain Android support.
564+
*/
565+
private static boolean isDecimalNotation(final String val) {
566+
return val.indexOf('.') > -1 || val.indexOf('e') > -1
567+
|| val.indexOf('E') > -1 || "-0".equals(val);
568+
}
569+
570+
499571
/**
500572
* Convert a well-formed (but not necessarily valid) XML string into a
501573
* JSONObject. Some information may be lost in this transformation because

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ public void commentsInXML() {
709709
@Test
710710
public void testToJSONArray_jsonOutput() {
711711
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
712-
final String expectedJsonString = "[\"root\",[\"id\",1],[\"id\",1],[\"id\",0],[\"id\",0],[\"item\",{\"id\":1}],[\"title\",true]]";
712+
final String expectedJsonString = "[\"root\",[\"id\",\"01\"],[\"id\",1],[\"id\",\"00\"],[\"id\",0],[\"item\",{\"id\":\"01\"}],[\"title\",true]]";
713713
final JSONArray actualJsonOutput = JSONML.toJSONArray(originalXml, false);
714714
assertEquals(expectedJsonString, actualJsonOutput.toString());
715715
}

0 commit comments

Comments
 (0)