Summary
The JsonNumber.of(double) factory method has a bug where it hardcodes decimalOffset=0 and exponentOffset=0 when creating the JsonNumberImpl. This causes toLong() to fail for integral doubles like 123.0.
Root Cause
In JsonNumber.java line 117:
static JsonNumber of(double num) {
if (!Double.isFinite(num)) {
throw new IllegalArgumentException("Not a valid JSON number");
}
var str = Double.toString(num);
return new JsonNumberImpl(str.toCharArray(), 0, str.length(), 0, 0);
// ^^^^^ BUG: hardcoded offsets
}
For Double.toString(123.0) which returns "123.0":
decimalOffset should be 3 (position of .) but is passed as 0
- This causes
initNumLong() in JsonNumberImpl to misparse the number
Reproduction
var jn = JsonNumber.of(123.0);
jn.toString(); // "123.0" - OK
jn.toDouble(); // 123.0 - OK
jn.toLong(); // THROWS: "123.0 cannot be represented as a long"
// EXPECTED: 123L (per API docs, "123.0" should be convertible)
Comparison with Json.parse()
var parsed = Json.parse("123.0");
parsed.toLong(); // 123L - WORKS correctly
The difference is that Json.parse() correctly computes the decimalOffset during parsing.
Suggested Fix
Option 1 - Reuse JsonNumber.of(String) which delegates to parser:
static JsonNumber of(double num) {
if (!Double.isFinite(num)) {
throw new IllegalArgumentException("Not a valid JSON number");
}
return JsonNumber.of(Double.toString(num));
}
Option 2 - Compute correct offsets:
static JsonNumber of(double num) {
if (!Double.isFinite(num)) {
throw new IllegalArgumentException("Not a valid JSON number");
}
var str = Double.toString(num);
var chars = str.toCharArray();
int dec = str.indexOf('.');
int exp = str.indexOf('e');
if (exp == -1) exp = str.indexOf('E');
return new JsonNumberImpl(chars, 0, chars.length,
dec == -1 ? -1 : dec,
exp == -1 ? -1 : exp);
}
This is an Upstream Bug
This bug exists in OpenJDK jdk-sandbox json branch at commit 91a479d. The upstream tests in TestJsonNumber.java do not cover this case - they test of(double).toDouble() but never of(double).toLong() for integral doubles.
Affected Versions
- jdk-sandbox json branch (upstream)
- java.util.json.Java21 backport 0.1.9 (this repo, synced from upstream)
Summary
The
JsonNumber.of(double)factory method has a bug where it hardcodesdecimalOffset=0andexponentOffset=0when creating theJsonNumberImpl. This causestoLong()to fail for integral doubles like123.0.Root Cause
In
JsonNumber.javaline 117:For
Double.toString(123.0)which returns"123.0":decimalOffsetshould be3(position of.) but is passed as0initNumLong()inJsonNumberImplto misparse the numberReproduction
Comparison with Json.parse()
The difference is that
Json.parse()correctly computes thedecimalOffsetduring parsing.Suggested Fix
Option 1 - Reuse
JsonNumber.of(String)which delegates to parser:Option 2 - Compute correct offsets:
This is an Upstream Bug
This bug exists in OpenJDK jdk-sandbox
jsonbranch at commit91a479d. The upstream tests inTestJsonNumber.javado not cover this case - they testof(double).toDouble()but neverof(double).toLong()for integral doubles.Affected Versions