Skip to content

Commit 7186065

Browse files
cursoragentsimbo1905
andcommitted
Issue #119 Document JsonNumber numeric conversion design choices
Co-authored-by: simbo1905 <simbo1905@60hertz.com>
1 parent 4b60358 commit 7186065

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

DESIGN_CHOICES.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Design choices: numeric handling (`JsonNumber`, BigDecimal/BigInteger)
2+
3+
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:
4+
5+
- 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.
6+
- When upstream removes or reshapes API, this repo follows.
7+
8+
## What changed (the “story”)
9+
10+
Older revisions of this backport carried some convenience entry points that accepted `java.math.BigDecimal` and `java.math.BigInteger` when building JSON numbers.
11+
12+
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.
13+
14+
Put differently: the design is “JSON numbers are text first”, not “JSON numbers are a Java numeric tower”.
15+
16+
## Why upstream prefers `String` (and why BigDecimal constructors are a footgun)
17+
18+
### 1) JSON numbers are arbitrary precision *text*
19+
20+
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:
21+
22+
- can be preserved exactly when parsed from a document
23+
- can be created from a string when you need exact control
24+
25+
### 2) `BigDecimal`/`BigInteger` introduce formatting policy into the API
26+
27+
If `JsonNumber` has `of(BigDecimal)` / `of(BigInteger)`:
28+
29+
- which textual form should be used (`toString()` vs `toPlainString()`)?
30+
- should `-0` be preserved, normalized, or rejected?
31+
- should `1e2` round-trip as `1e2` or normalize to `100`?
32+
33+
Any choice becomes a **semantic commitment**: it changes `toString()`, equality and hash behavior, and round-trip characteristics.
34+
35+
Upstream avoids baking those policy decisions into the core JSON API by:
36+
37+
- providing `JsonNumber.of(String)` as the “I know what text I want” factory
38+
- documenting that you can always interoperate with arbitrary precision Java numerics by converting *from* `toString()`
39+
40+
This intent is explicitly documented in `JsonNumber`’s own `@apiNote`.
41+
42+
### 3) Minimal factories avoid overload explosion
43+
44+
JSON object/array construction in this API already leans toward:
45+
46+
- immutable values
47+
- static factories (`...of(...)`)
48+
- pattern matching / sealed types when consuming values
49+
50+
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.
51+
52+
## Recommended recipes (lossless + explicit)
53+
54+
### Parse → BigDecimal (lossless)
55+
56+
```java
57+
var n = (JsonNumber) Json.parse("3.141592653589793238462643383279");
58+
var bd = new BigDecimal(n.toString()); // exact
59+
```
60+
61+
### Parse → BigInteger (lossless, when integral)
62+
63+
```java
64+
var n = (JsonNumber) Json.parse("1.23e2");
65+
var bi = new BigDecimal(n.toString()).toBigIntegerExact(); // 123
66+
```
67+
68+
### BigDecimal → JsonNumber (pick your textual policy)
69+
70+
If you want to preserve the *mathematical* value without scientific notation:
71+
72+
```java
73+
var bd = new BigDecimal("1000");
74+
var n = JsonNumber.of(bd.toPlainString()); // "1000"
75+
```
76+
77+
If you’re fine with scientific notation when `BigDecimal` chooses it:
78+
79+
```java
80+
var bd = new BigDecimal("1E+3");
81+
var n = JsonNumber.of(bd.toString()); // "1E+3" (still valid JSON number text)
82+
```
83+
84+
### JSON lexical preservation is not numeric normalization
85+
86+
Two JSON numbers can represent the same numeric value but still be different JSON texts:
87+
88+
```java
89+
var a = (JsonNumber) Json.parse("1e2");
90+
var b = (JsonNumber) Json.parse("100");
91+
assert !a.toString().equals(b.toString()); // lexical difference preserved
92+
```
93+
94+
If 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.
95+
96+
## Runnable examples
97+
98+
This document’s examples are mirrored in code:
99+
100+
- `json-java21/src/test/java/jdk/sandbox/java/util/json/examples/DesignChoicesExamples.java`
101+
- `json-java21/src/test/java/jdk/sandbox/java/util/json/DesignChoicesNumberExamplesTest.java`
102+

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ java -cp ./json-java21/target/java.util.json-*.jar:./json-java21/target/test-cla
2727

2828
*Replace `*` with the actual version number from the JAR filename.*
2929

30+
## Design notes
31+
32+
- **Numeric handling (JsonNumber, BigDecimal/BigInteger)**: see `DESIGN_CHOICES.md`
33+
3034
## API Overview
3135

3236
The API provides immutable JSON value types:

0 commit comments

Comments
 (0)