Skip to content

Commit 2f2cd4d

Browse files
author
John J. Aylward
committed
Fix for number output bug.
java.lang.Number is currently output without any validation. For all java.* Numbers, this is fine, but for custom Number implementations like Complex or Fraction, the resulting JSON output may be invalid. For example: If a Fraction implementation defines its' toString method as `return numerator + "/" + denominator`, then the resulting JSON output would be something like this: ```json { "fraction" : 1/2 } ``` This is not valid JSON. This commit verifies that the string representation of the number is close to a JSON formatted number by use of the BigDecimal constructor. If the constructor throws a NumberFormatException, then the string value is instead quoted as a string. The example above would instead output like the following: ```json { "fraction" : "1/2" } ```
1 parent 7232a95 commit 2f2cd4d

File tree

1 file changed

+37
-14
lines changed

1 file changed

+37
-14
lines changed

JSONObject.java

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ public static String numberToString(Number number) throws JSONException {
838838
}
839839
testValidity(number);
840840

841-
// Shave off trailing zeros and decimal point, if possible.
841+
// Shave off trailing zeros and decimal point, if possible.
842842

843843
String string = number.toString();
844844
if (string.indexOf('.') > 0 && string.indexOf('e') < 0
@@ -1693,7 +1693,18 @@ public static String valueToString(Object value) throws JSONException {
16931693
throw new JSONException("Bad value from toJSONString: " + object);
16941694
}
16951695
if (value instanceof Number) {
1696-
return numberToString((Number) value);
1696+
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
1697+
final String numberAsString = numberToString((Number) value);
1698+
try {
1699+
// Use the BigDecimal constructor for it's parser to validate the format.
1700+
new BigDecimal(numberAsString);
1701+
// Close enough to a JSON number that we will return it unquoted
1702+
return numberAsString;
1703+
} catch (NumberFormatException ex){
1704+
// The Number value is not a valid JSON number.
1705+
// Instead we will quote it as a string
1706+
return quote(numberAsString);
1707+
}
16971708
}
16981709
if (value instanceof Boolean || value instanceof JSONObject
16991710
|| value instanceof JSONArray) {
@@ -1783,6 +1794,30 @@ static final Writer writeValue(Writer writer, Object value,
17831794
int indentFactor, int indent) throws JSONException, IOException {
17841795
if (value == null || value.equals(null)) {
17851796
writer.write("null");
1797+
} else if (value instanceof JSONString) {
1798+
Object o;
1799+
try {
1800+
o = ((JSONString) value).toJSONString();
1801+
} catch (Exception e) {
1802+
throw new JSONException(e);
1803+
}
1804+
writer.write(o != null ? o.toString() : quote(value.toString()));
1805+
} else if (value instanceof Number) {
1806+
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
1807+
final String numberAsString = numberToString((Number) value);
1808+
try {
1809+
// Use the BigDecimal constructor for it's parser to validate the format.
1810+
@SuppressWarnings("unused")
1811+
BigDecimal testNum = new BigDecimal(numberAsString);
1812+
// Close enough to a JSON number that we will use it unquoted
1813+
writer.write(numberAsString);
1814+
} catch (NumberFormatException ex){
1815+
// The Number value is not a valid JSON number.
1816+
// Instead we will quote it as a string
1817+
quote(numberAsString, writer);
1818+
}
1819+
} else if (value instanceof Boolean) {
1820+
writer.write(value.toString());
17861821
} else if (value instanceof JSONObject) {
17871822
((JSONObject) value).write(writer, indentFactor, indent);
17881823
} else if (value instanceof JSONArray) {
@@ -1795,18 +1830,6 @@ static final Writer writeValue(Writer writer, Object value,
17951830
new JSONArray(coll).write(writer, indentFactor, indent);
17961831
} else if (value.getClass().isArray()) {
17971832
new JSONArray(value).write(writer, indentFactor, indent);
1798-
} else if (value instanceof Number) {
1799-
writer.write(numberToString((Number) value));
1800-
} else if (value instanceof Boolean) {
1801-
writer.write(value.toString());
1802-
} else if (value instanceof JSONString) {
1803-
Object o;
1804-
try {
1805-
o = ((JSONString) value).toJSONString();
1806-
} catch (Exception e) {
1807-
throw new JSONException(e);
1808-
}
1809-
writer.write(o != null ? o.toString() : quote(value.toString()));
18101833
} else {
18111834
quote(value.toString(), writer);
18121835
}

0 commit comments

Comments
 (0)