@@ -45,6 +45,7 @@ of this software and associated documentation files (the "Software"), to deal
4545import java .util .Map .Entry ;
4646import java .util .ResourceBundle ;
4747import 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 );
0 commit comments