This repository is a backport of the upstream OpenJDK sandbox java.util.json work (mirrored here as jdk.sandbox.java.util.json). That matters for “why did X disappear?” questions:
- This repo intentionally avoids inventing new public API shapes that diverge from upstream, because doing so makes syncing harder and breaks the point of the backport.
- When upstream removes or reshapes API, this repo follows.
Older revisions of this backport carried some convenience entry points that accepted java.math.BigDecimal and java.math.BigInteger when building JSON numbers.
During the last upstream sync, those entry points were removed. That is consistent with the upstream design direction: keep JsonNumber’s public surface area small and make lossless numeric interoperability flow through the lexical JSON number text (JsonNumber.toString()), not through a growing matrix of overloads.
Put differently: the design is “JSON numbers are text first”, not “JSON numbers are a Java numeric tower”.
RFC 8259 defines the syntax of a JSON number; it does not define a fixed precision or a single canonical format. The API aligns with that by treating the number as a string that:
- can be preserved exactly when parsed from a document
- can be created from a string when you need exact control
If JsonNumber has of(BigDecimal) / of(BigInteger):
- which textual form should be used (
toString()vstoPlainString())? - should
-0be preserved, normalized, or rejected? - should
1e2round-trip as1e2or normalize to100?
Any choice becomes a semantic commitment: it changes toString(), equality and hash behavior, and round-trip characteristics.
Upstream avoids baking those policy decisions into the core JSON API by:
- providing
JsonNumber.of(String)as the “I know what text I want” factory - documenting that you can always interoperate with arbitrary precision Java numerics by converting from
toString()
This intent is explicitly documented in JsonNumber’s own @apiNote.
JSON object/array construction in this API already leans toward:
- immutable values
- static factories (
...of(...)) - pattern matching / sealed types when consuming values
That style is a natural fit for “a few sharp entry points” rather than the legacy OO pattern of ever-expanding constructor overloads for every “convenient” numeric type.
var n = (JsonNumber) Json.parse("3.141592653589793238462643383279");
var bd = new BigDecimal(n.toString()); // exactvar n = (JsonNumber) Json.parse("1.23e2");
var bi = new BigDecimal(n.toString()).toBigIntegerExact(); // 123If you want to preserve the mathematical value without scientific notation:
var bd = new BigDecimal("1000");
var n = JsonNumber.of(bd.toPlainString()); // "1000"If you’re fine with scientific notation when BigDecimal chooses it:
var bd = new BigDecimal("1E+3");
var n = JsonNumber.of(bd.toString()); // "1E+3" (still valid JSON number text)Two JSON numbers can represent the same numeric value but still be different JSON texts:
var a = (JsonNumber) Json.parse("1e2");
var b = (JsonNumber) Json.parse("100");
assert !a.toString().equals(b.toString()); // lexical difference preservedIf your application needs numeric equality or canonicalization, perform it explicitly with BigDecimal (or your own policy), rather than relying on the JSON value object to do it implicitly.
This document’s examples are mirrored in code:
json-java21/src/test/java/jdk/sandbox/java/util/json/examples/DesignChoicesExamples.javajson-java21/src/test/java/jdk/sandbox/java/util/json/DesignChoicesNumberExamplesTest.java