Skip to content

Commit a63fa03

Browse files
author
John J. Aylward
committed
* Fixes opt/getBigDecimal to be consistent
* Performance: Updates JSONWriter to use a regex to decide if writing as a number is best.
1 parent 16225ef commit a63fa03

3 files changed

Lines changed: 69 additions & 85 deletions

File tree

JSONArray.java

Lines changed: 10 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,12 @@ public <E extends Enum<E>> E getEnum(Class<E> clazz, int index) throws JSONExcep
345345
*/
346346
public BigDecimal getBigDecimal (int index) throws JSONException {
347347
Object object = this.get(index);
348-
try {
349-
return new BigDecimal(object.toString());
350-
} catch (Exception e) {
348+
BigDecimal val = JSONObject.objectToBigDecimal(object, null);
349+
if(val == null) {
351350
throw new JSONException("JSONArray[" + index +
352-
"] could not convert to BigDecimal.", e);
351+
"] could not convert to BigDecimal ("+ object + ").");
353352
}
353+
return val;
354354
}
355355

356356
/**
@@ -365,12 +365,12 @@ public BigDecimal getBigDecimal (int index) throws JSONException {
365365
*/
366366
public BigInteger getBigInteger (int index) throws JSONException {
367367
Object object = this.get(index);
368-
try {
369-
return new BigInteger(object.toString());
370-
} catch (Exception e) {
368+
BigInteger val = JSONObject.objectToBigInteger(object, null);
369+
if(val == null) {
371370
throw new JSONException("JSONArray[" + index +
372-
"] could not convert to BigInteger.", e);
371+
"] could not convert to BigDecimal ("+ object + ").");
373372
}
373+
return val;
374374
}
375375

376376
/**
@@ -739,31 +739,7 @@ public <E extends Enum<E>> E optEnum(Class<E> clazz, int index, E defaultValue)
739739
*/
740740
public BigInteger optBigInteger(int index, BigInteger defaultValue) {
741741
Object val = this.opt(index);
742-
if (JSONObject.NULL.equals(val)) {
743-
return defaultValue;
744-
}
745-
if (val instanceof BigInteger){
746-
return (BigInteger) val;
747-
}
748-
if (val instanceof BigDecimal){
749-
return ((BigDecimal) val).toBigInteger();
750-
}
751-
if (val instanceof Double || val instanceof Float){
752-
return new BigDecimal(((Number) val).doubleValue()).toBigInteger();
753-
}
754-
if (val instanceof Long || val instanceof Integer
755-
|| val instanceof Short || val instanceof Byte){
756-
return BigInteger.valueOf(((Number) val).longValue());
757-
}
758-
try {
759-
final String valStr = val.toString();
760-
if(JSONObject.isDecimalNotation(valStr)) {
761-
return new BigDecimal(valStr).toBigInteger();
762-
}
763-
return new BigInteger(valStr);
764-
} catch (Exception e) {
765-
return defaultValue;
766-
}
742+
return JSONObject.objectToBigInteger(val, defaultValue);
767743
}
768744

769745
/**
@@ -779,27 +755,7 @@ public BigInteger optBigInteger(int index, BigInteger defaultValue) {
779755
*/
780756
public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
781757
Object val = this.opt(index);
782-
if (JSONObject.NULL.equals(val)) {
783-
return defaultValue;
784-
}
785-
if (val instanceof BigDecimal){
786-
return (BigDecimal) val;
787-
}
788-
if (val instanceof BigInteger){
789-
return new BigDecimal((BigInteger) val);
790-
}
791-
if (val instanceof Double || val instanceof Float){
792-
return new BigDecimal(((Number) val).doubleValue());
793-
}
794-
if (val instanceof Long || val instanceof Integer
795-
|| val instanceof Short || val instanceof Byte){
796-
return new BigDecimal(((Number) val).longValue());
797-
}
798-
try {
799-
return new BigDecimal(val.toString());
800-
} catch (Exception e) {
801-
return defaultValue;
802-
}
758+
return JSONObject.objectToBigDecimal(val, defaultValue);
803759
}
804760

805761
/**

JSONObject.java

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ of this software and associated documentation files (the "Software"), to deal
4545
import java.util.Map.Entry;
4646
import java.util.ResourceBundle;
4747
import java.util.Set;
48+
import java.util.regex.Pattern;
4849

4950
/**
5051
* A JSONObject is an unordered collection of name/value pairs. Its external
@@ -150,6 +151,12 @@ public String toString() {
150151
return "null";
151152
}
152153
}
154+
155+
/**
156+
* Regular Expression Pattern that matches JSON Numbers. This is primarily used for
157+
* output to guarantee that we are always writing valid JSON.
158+
*/
159+
static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?");
153160

154161
/**
155162
* The map where the JSONObject's properties are kept.
@@ -630,16 +637,19 @@ public boolean getBoolean(String key) throws JSONException {
630637
*/
631638
public BigInteger getBigInteger(String key) throws JSONException {
632639
Object object = this.get(key);
633-
try {
634-
return new BigInteger(object.toString());
635-
} catch (Exception e) {
636-
throw new JSONException("JSONObject[" + quote(key)
637-
+ "] could not be converted to BigInteger.", e);
640+
BigInteger ret = objectToBigInteger(object, null);
641+
if (ret != null) {
642+
return ret;
638643
}
644+
throw new JSONException("JSONObject[" + quote(key)
645+
+ "] could not be converted to BigInteger (" + object + ").");
639646
}
640647

641648
/**
642-
* Get the BigDecimal value associated with a key.
649+
* Get the BigDecimal value associated with a key. If the value is float or
650+
* double, the the {@link BigDecimal#BigDecimal(double)} constructor will
651+
* be used. See notes on the constructor for conversion issues that may
652+
* arise.
643653
*
644654
* @param key
645655
* A key string.
@@ -650,15 +660,12 @@ public BigInteger getBigInteger(String key) throws JSONException {
650660
*/
651661
public BigDecimal getBigDecimal(String key) throws JSONException {
652662
Object object = this.get(key);
653-
if (object instanceof BigDecimal) {
654-
return (BigDecimal)object;
655-
}
656-
try {
657-
return new BigDecimal(object.toString());
658-
} catch (Exception e) {
659-
throw new JSONException("JSONObject[" + quote(key)
660-
+ "] could not be converted to BigDecimal.", e);
663+
BigDecimal ret = objectToBigDecimal(object, null);
664+
if (ret != null) {
665+
return ret;
661666
}
667+
throw new JSONException("JSONObject[" + quote(key)
668+
+ "] could not be converted to BigDecimal (" + object + ").");
662669
}
663670

664671
/**
@@ -968,7 +975,7 @@ public int length() {
968975
* @return true if JSONObject is empty, otherwise false.
969976
*/
970977
public boolean isEmpty() {
971-
return map.isEmpty();
978+
return this.map.isEmpty();
972979
}
973980

974981
/**
@@ -1113,7 +1120,10 @@ public boolean optBoolean(String key, boolean defaultValue) {
11131120
/**
11141121
* Get an optional BigDecimal associated with a key, or the defaultValue if
11151122
* there is no such key or if its value is not a number. If the value is a
1116-
* string, an attempt will be made to evaluate it as a number.
1123+
* string, an attempt will be made to evaluate it as a number. If the value
1124+
* is float or double, then the {@link BigDecimal#BigDecimal(double)}
1125+
* constructor will be used. See notes on the constructor for conversion
1126+
* issues that may arise.
11171127
*
11181128
* @param key
11191129
* A key string.
@@ -1123,6 +1133,15 @@ public boolean optBoolean(String key, boolean defaultValue) {
11231133
*/
11241134
public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
11251135
Object val = this.opt(key);
1136+
return objectToBigDecimal(val, defaultValue);
1137+
}
1138+
1139+
/**
1140+
* @param defaultValue
1141+
* @param val
1142+
* @return
1143+
*/
1144+
static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) {
11261145
if (NULL.equals(val)) {
11271146
return defaultValue;
11281147
}
@@ -1133,6 +1152,10 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
11331152
return new BigDecimal((BigInteger) val);
11341153
}
11351154
if (val instanceof Double || val instanceof Float){
1155+
final double d = ((Number) val).doubleValue();
1156+
if(Double.isNaN(d)) {
1157+
return defaultValue;
1158+
}
11361159
return new BigDecimal(((Number) val).doubleValue());
11371160
}
11381161
if (val instanceof Long || val instanceof Integer
@@ -1160,6 +1183,15 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
11601183
*/
11611184
public BigInteger optBigInteger(String key, BigInteger defaultValue) {
11621185
Object val = this.opt(key);
1186+
return objectToBigInteger(val, defaultValue);
1187+
}
1188+
1189+
/**
1190+
* @param defaultValue
1191+
* @param val
1192+
* @return
1193+
*/
1194+
static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) {
11631195
if (NULL.equals(val)) {
11641196
return defaultValue;
11651197
}
@@ -1170,7 +1202,11 @@ public BigInteger optBigInteger(String key, BigInteger defaultValue) {
11701202
return ((BigDecimal) val).toBigInteger();
11711203
}
11721204
if (val instanceof Double || val instanceof Float){
1173-
return new BigDecimal(((Number) val).doubleValue()).toBigInteger();
1205+
final double d = ((Number) val).doubleValue();
1206+
if(Double.isNaN(d)) {
1207+
return defaultValue;
1208+
}
1209+
return new BigDecimal(d).toBigInteger();
11741210
}
11751211
if (val instanceof Long || val instanceof Integer
11761212
|| val instanceof Short || val instanceof Byte){
@@ -2414,13 +2450,9 @@ static final Writer writeValue(Writer writer, Object value,
24142450
} else if (value instanceof Number) {
24152451
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
24162452
final String numberAsString = numberToString((Number) value);
2417-
try {
2418-
// Use the BigDecimal constructor for its parser to validate the format.
2419-
@SuppressWarnings("unused")
2420-
BigDecimal testNum = new BigDecimal(numberAsString);
2421-
// Close enough to a JSON number that we will use it unquoted
2453+
if(NUMBER_PATTERN.matcher(numberAsString).matches()) {
24222454
writer.write(numberAsString);
2423-
} catch (NumberFormatException ex){
2455+
} else {
24242456
// The Number value is not a valid JSON number.
24252457
// Instead we will quote it as a string
24262458
quote(numberAsString, writer);

JSONWriter.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -340,17 +340,13 @@ public static String valueToString(Object value) throws JSONException {
340340
if (value instanceof Number) {
341341
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
342342
final String numberAsString = JSONObject.numberToString((Number) value);
343-
try {
344-
// Use the BigDecimal constructor for it's parser to validate the format.
345-
@SuppressWarnings("unused")
346-
BigDecimal unused = new BigDecimal(numberAsString);
343+
if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) {
347344
// Close enough to a JSON number that we will return it unquoted
348345
return numberAsString;
349-
} catch (NumberFormatException ex){
350-
// The Number value is not a valid JSON number.
351-
// Instead we will quote it as a string
352-
return JSONObject.quote(numberAsString);
353346
}
347+
// The Number value is not a valid JSON number.
348+
// Instead we will quote it as a string
349+
return JSONObject.quote(numberAsString);
354350
}
355351
if (value instanceof Boolean || value instanceof JSONObject
356352
|| value instanceof JSONArray) {

0 commit comments

Comments
 (0)