1
0
Fork 0
mirror of https://github.com/ethauvin/JSON-java.git synced 2025-06-17 07:50:52 -07:00

updates the getNumber/optNumber to not return invalid Doubles

This commit is contained in:
John J. Aylward 2017-05-18 19:49:50 -04:00
parent a7f8ff24df
commit 849b392c01
2 changed files with 54 additions and 30 deletions

View file

@ -738,7 +738,11 @@ public class JSONArray implements Iterable<Object> {
return BigInteger.valueOf(((Number) val).longValue()); return BigInteger.valueOf(((Number) val).longValue());
} }
try { try {
return new BigDecimal(val.toString()).toBigInteger(); final String valStr = val.toString();
if(JSONObject.isDecimalNotation(valStr)) {
return new BigDecimal(valStr).toBigInteger();
}
return new BigInteger(valStr);
} catch (Exception e) { } catch (Exception e) {
return defaultValue; return defaultValue;
} }

View file

@ -1068,7 +1068,11 @@ public class JSONObject {
// jo.optInt("double"); -- will return 1, not an error // jo.optInt("double"); -- will return 1, not an error
// this conversion to BigDecimal then to BigInteger is to maintain // this conversion to BigDecimal then to BigInteger is to maintain
// that type cast support that may truncate the decimal. // that type cast support that may truncate the decimal.
return new BigDecimal(val.toString()).toBigInteger(); final String valStr = val.toString();
if(isDecimalNotation(valStr)) {
return new BigDecimal(valStr).toBigInteger();
}
return new BigInteger(valStr);
} catch (Exception e) { } catch (Exception e) {
return defaultValue; return defaultValue;
} }
@ -1759,12 +1763,22 @@ public class JSONObject {
return false; return false;
} }
} }
/**
* Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
*
* @param val value to test
* @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise.
*/
protected static boolean isDecimalNotation(final String val) {
return val.indexOf('.') > -1 || val.indexOf('e') > -1
|| val.indexOf('E') > -1 || "-0".equals(val);
}
/** /**
* Converts a string to a number using the narrowest possible type. Possible * Converts a string to a number using the narrowest possible type. Possible
* returns for this function are BigDecimal, Double, BigInteger, Long, and Integer. * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
* * When a Double is returned, it should always be a valid Double and not NaN or +-infinity.
* An Exception is thrown if
* *
* @param val value to convert * @param val value to convert
* @return Number representation of the value. * @return Number representation of the value.
@ -1775,46 +1789,52 @@ public class JSONObject {
char initial = val.charAt(0); char initial = val.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') { if ((initial >= '0' && initial <= '9') || initial == '-') {
// decimal representation // decimal representation
if (val.indexOf('.') > -1 || val.indexOf('e') > -1 if (isDecimalNotation(val)) {
|| val.indexOf('E') > -1
|| "-0".equals(val)) {
// quick dirty way to see if we need a BigDecimal instead of a Double // quick dirty way to see if we need a BigDecimal instead of a Double
// this only handles some cases of overflow or underflow
if (val.length()>14) { if (val.length()>14) {
return new BigDecimal(val); return new BigDecimal(val);
} }
return Double.valueOf(val); final Double d = Double.valueOf(val);
if (d.isInfinite() || d.isNaN()) {
// if we can't parse it as a double, go up to BigDecimal
// this is probably due to underflow like 4.32e-678
// or overflow like 4.65e5324. The size of the string is small
// but can't be held in a Double.
return new BigDecimal(val);
}
return d;
} }
// integer representation. // integer representation.
// This will narrow any values to the smallest reasonable Object representation // This will narrow any values to the smallest reasonable Object representation
// (Integer, Long, or BigInteger) // (Integer, Long, or BigInteger)
// string version
// The compare string length method reduces GC, // The compare string length method reduces GC,
// but leads to smaller integers being placed in larger wrappers even though not // but leads to smaller integers being placed in larger wrappers even though not
// needed. i.e. 1,000,000,000 -> Long even though it's an Integer // needed. i.e. 1,000,000,000 -> Long even though it's an Integer
// 1,000,000,000,000,000,000 -> BigInteger even though it's a Long // 1,000,000,000,000,000,000 -> BigInteger even though it's a Long
//if(val.length()<=9){
// string version // return Integer.valueOf(val);
if(val.length()<=9){ //}
return Integer.valueOf(val); //if(val.length()<=18){
} // return Long.valueOf(val);
if(val.length()<=18){ //}
return Long.valueOf(val); //return new BigInteger(val);
}
return new BigInteger(val);
// BigInteger version: We use a similar bitLenth compare as // BigInteger version: We use a similar bitLenth compare as
// BigInteger#intValueExact uses. Increases GC, but objects hold // BigInteger#intValueExact uses. Increases GC, but objects hold
// only what they need. i.e. Less runtime overhead if the value is // only what they need. i.e. Less runtime overhead if the value is
// long lived. Which is the better tradeoff? This is closer to what's // long lived. Which is the better tradeoff? This is closer to what's
// in stringToValue. // in stringToValue.
BigInteger bi = new BigInteger(val);
//BigInteger bi = new BigInteger((String)val); if(bi.bitLength()<=31){
//if(bi.bitLength()<=31){ return Integer.valueOf(bi.intValue());
// return Integer.valueOf(bi.intValue()); }
//} if(bi.bitLength()<=63){
//if(bi.bitLength()<=63){ return Long.valueOf(bi.longValue());
// return Long.valueOf(bi.longValue()); }
//} return bi;
//return bi;
} }
throw new NumberFormatException("val ["+val+"] is not a valid number."); throw new NumberFormatException("val ["+val+"] is not a valid number.");
} }
@ -1849,9 +1869,9 @@ public class JSONObject {
char initial = string.charAt(0); char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') { if ((initial >= '0' && initial <= '9') || initial == '-') {
try { try {
if (string.indexOf('.') > -1 || string.indexOf('e') > -1 // if we want full Big Number support this block can be replaced with:
|| string.indexOf('E') > -1 // return stringToNumber(string);
|| "-0".equals(string)) { if (isDecimalNotation(string)) {
Double d = Double.valueOf(string); Double d = Double.valueOf(string);
if (!d.isInfinite() && !d.isNaN()) { if (!d.isInfinite() && !d.isNaN()) {
return d; return d;