Skip to content

Commit

Permalink
Fix for number output bug.
Browse files Browse the repository at this point in the history
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" }
```
  • Loading branch information
John J. Aylward committed Aug 17, 2016
1 parent 7232a95 commit 2f2cd4d
Showing 1 changed file with 37 additions and 14 deletions.
51 changes: 37 additions & 14 deletions JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ public static String numberToString(Number number) throws JSONException {
}
testValidity(number);

// Shave off trailing zeros and decimal point, if possible.
// Shave off trailing zeros and decimal point, if possible.

String string = number.toString();
if (string.indexOf('.') > 0 && string.indexOf('e') < 0
Expand Down Expand Up @@ -1693,7 +1693,18 @@ public static String valueToString(Object value) throws JSONException {
throw new JSONException("Bad value from toJSONString: " + object);
}
if (value instanceof Number) {
return numberToString((Number) value);
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
final String numberAsString = numberToString((Number) value);
try {
// Use the BigDecimal constructor for it's parser to validate the format.
new BigDecimal(numberAsString);
// Close enough to a JSON number that we will return it unquoted
return numberAsString;
} catch (NumberFormatException ex){
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
return quote(numberAsString);
}
}
if (value instanceof Boolean || value instanceof JSONObject
|| value instanceof JSONArray) {
Expand Down Expand Up @@ -1783,6 +1794,30 @@ static final Writer writeValue(Writer writer, Object value,
int indentFactor, int indent) throws JSONException, IOException {
if (value == null || value.equals(null)) {
writer.write("null");
} else if (value instanceof JSONString) {
Object o;
try {
o = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
writer.write(o != null ? o.toString() : quote(value.toString()));
} else if (value instanceof Number) {
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
final String numberAsString = numberToString((Number) value);
try {
// Use the BigDecimal constructor for it's parser to validate the format.
@SuppressWarnings("unused")
BigDecimal testNum = new BigDecimal(numberAsString);
// Close enough to a JSON number that we will use it unquoted
writer.write(numberAsString);
} catch (NumberFormatException ex){
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
quote(numberAsString, writer);
}
} else if (value instanceof Boolean) {
writer.write(value.toString());
} else if (value instanceof JSONObject) {
((JSONObject) value).write(writer, indentFactor, indent);
} else if (value instanceof JSONArray) {
Expand All @@ -1795,18 +1830,6 @@ static final Writer writeValue(Writer writer, Object value,
new JSONArray(coll).write(writer, indentFactor, indent);
} else if (value.getClass().isArray()) {
new JSONArray(value).write(writer, indentFactor, indent);
} else if (value instanceof Number) {
writer.write(numberToString((Number) value));
} else if (value instanceof Boolean) {
writer.write(value.toString());
} else if (value instanceof JSONString) {
Object o;
try {
o = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
writer.write(o != null ? o.toString() : quote(value.toString()));
} else {
quote(value.toString(), writer);
}
Expand Down

0 comments on commit 2f2cd4d

Please sign in to comment.