Skip to content

Commit 698c939

Browse files
committed
Issue #144 Sync to last java.util.json sandbox commit c1a4f80 (2026-02-05)
This is the final sync of the java.util.json API before upstream moved to jdk.incubator.json. Changes from upstream since our last sync: - Added JsonValue.toInt() and JsonNumber.toInt() methods - Improved Javadoc for all conversion methods with @implSpec sections - Reordered valueOrNull() to end of JsonValue interface - Minor wording improvements in error messages Local changes preserved: - JsonNumber.of(double) bug fix (delegate to of(String) for correct offsets) - Utils.powExact() polyfill (Math.powExact not available in Java 21) - StableValue.java polyfill - JsonAssertionException.java All 27 tests pass. Upstream source: openjdk/jdk-sandbox@c1a4f80 Branch: json (last commit before preview->incubator move)
1 parent d943174 commit 698c939

18 files changed

Lines changed: 212 additions & 102 deletions

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -285,22 +285,27 @@ The test data is bundled as ZIP files and extracted automatically at runtime:
285285

286286
## Current Status
287287

288-
This code (as of 2026-01-25) is derived from the OpenJDK jdk-sandbox repository "json" branch. Key API changes from the previous version include:
289-
- `JsonString.value()``JsonString.string()`
290-
- `JsonNumber.toNumber()``JsonNumber.toLong()` / `JsonNumber.toDouble()`
291-
- `JsonBoolean.value()``JsonBoolean.bool()`
292-
- `JsonArray.values()``JsonArray.elements()`
293-
- `Json.fromUntyped()` and `Json.toUntyped()` have been removed
294-
- New accessor methods on `JsonValue`: `get(String)`, `element(int)`, `getOrAbsent(String)`, `valueOrNull()`
295-
- Internal implementation changed from `StableValue` to `LazyConstant`
288+
**Final `java.util.json` sandbox-era release** (2026-05-19).
289+
290+
This code is derived from the OpenJDK jdk-sandbox repository "json" branch at commit `c1a4f80` (2026-02-05), which was the last commit before the API was moved to `jdk.incubator.json`.
291+
292+
### API Summary
293+
- `JsonValue` conversion methods: `asBoolean()`, `toInt()`, `toLong()`, `toDouble()`, `asString()`
294+
- `JsonValue` navigation methods: `get(String)`, `get(int)`, `getOrAbsent(String)`, `valueOrNull()`
295+
- `JsonArray`: `elements()`, `of(List)`
296+
- `JsonObject`: `members()`, `of(Map)`
297+
- `Json`: `parse(String)`, `parse(char[])`, `toDisplayString(JsonValue, int)`
298+
299+
### Upstream Migration Notice
300+
The upstream `java.util.json` API has been promoted to `jdk.incubator.json` (commit `b956ae0`, 2026-02-05). The incubator version introduces significant API changes including method renames (`bool()``asBoolean()`, `string()``asString()`, etc.) and new methods (`asInt()`). A separate branch tracks the incubator upgrade — see issue #145.
296301

297302
The original proposal and design rationale can be found in the included PDF: [Towards a JSON API for the JDK.pdf](Towards%20a%20JSON%20API%20for%20the%20JDK.pdf)
298303

299304
The JSON compatibitlity tests in this repo suggest 99% conformance with a leading test suite when in "strict" mode. The two conformance expecatations that fail assume that duplicated keys in a JSON document are okay. The upstream code at this time appear to take a strict stance that it should not siliently ignore duplicate keys in a json object.
300305

301306
### CI: Upstream API Tracking
302307

303-
A daily workflow runs an API comparison against the OpenJDK sandbox and prints a JSON report. Implication: differences do not currently fail the build or auto‑open issues; check the workflow logs (or adjust the workflow to fail on diffs) if you need notifications.
308+
**Note**: The daily API tracker workflow currently targets the old `java.util.json` paths which no longer exist upstream. It needs to be updated to track `jdk.incubator.json` — see issue #145.
304309

305310
## Modifications
306311

json-java21/src/main/java/jdk/sandbox/internal/util/json/JsonArrayImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
import jdk.sandbox.java.util.json.JsonArray;
3232
import jdk.sandbox.java.util.json.JsonValue;
33-
3433
/**
3534
* JsonArray implementation class
3635
*/

json-java21/src/main/java/jdk/sandbox/internal/util/json/JsonBooleanImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
package jdk.sandbox.internal.util.json;
2727

2828
import jdk.sandbox.java.util.json.JsonBoolean;
29-
3029
/**
3130
* JsonBoolean implementation class
3231
*/

json-java21/src/main/java/jdk/sandbox/internal/util/json/JsonNullImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
package jdk.sandbox.internal.util.json;
2727

2828
import jdk.sandbox.java.util.json.JsonNull;
29-
3029
/**
3130
* JsonNull implementation class
3231
*/

json-java21/src/main/java/jdk/sandbox/internal/util/json/JsonNumberImpl.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929

3030
import java.util.Optional;
3131
import jdk.sandbox.java.util.json.JsonNumber;
32-
33-
3432
/**
3533
* JsonNumber implementation class
3634
*/
@@ -43,6 +41,7 @@ public final class JsonNumberImpl implements JsonNumber, JsonValueImpl {
4341
private final int exponentOffset;
4442

4543
private final LazyConstant<String> numString = LazyConstant.of(this::initNumString);
44+
private final LazyConstant<Optional<Integer>> numInteger = LazyConstant.of(this::initNumInteger);
4645
private final LazyConstant<Optional<Long>> numLong = LazyConstant.of(this::initNumLong);
4746
private final LazyConstant<Optional<Double>> numDouble = LazyConstant.of(this::initNumDouble);
4847

@@ -54,6 +53,12 @@ public JsonNumberImpl(char[] doc, int start, int end, int dec, int exp) {
5453
exponentOffset = exp;
5554
}
5655

56+
@Override
57+
public int toInt() {
58+
return numInteger.get().orElseThrow(() ->
59+
Utils.composeError(this, this + " cannot be represented as an int."));
60+
}
61+
5762
@Override
5863
public long toLong() {
5964
return numLong.get().orElseThrow(() ->
@@ -97,6 +102,14 @@ private String initNumString() {
97102
return new String(doc, startOffset, endOffset - startOffset);
98103
}
99104

105+
private Optional<Integer> initNumInteger() {
106+
try {
107+
return numLong.get().map(Math::toIntExact);
108+
} catch(ArithmeticException e) {
109+
return Optional.empty();
110+
}
111+
}
112+
100113
// 4 cases: Fully integral, has decimal, has exponent, has decimal and exponent
101114
private Optional<Long> initNumLong() {
102115
try {
@@ -147,7 +160,7 @@ private Optional<Long> initNumLong() {
147160
startOffset, decimalOffset - startOffset), 10));
148161
}
149162
}
150-
} catch (NumberFormatException | ArithmeticException ignored) {}
163+
} catch(NumberFormatException | ArithmeticException e) {}
151164
return Optional.empty();
152165
}
153166

json-java21/src/main/java/jdk/sandbox/internal/util/json/JsonObjectImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
import jdk.sandbox.java.util.json.JsonObject;
3232
import jdk.sandbox.java.util.json.JsonValue;
33-
3433
/**
3534
* JsonObject implementation class
3635
*/

json-java21/src/main/java/jdk/sandbox/internal/util/json/JsonStringImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
package jdk.sandbox.internal.util.json;
2727

2828
import jdk.sandbox.java.util.json.JsonString;
29-
3029
/**
3130
* JsonString implementation class
3231
*/

json-java21/src/main/java/jdk/sandbox/internal/util/json/Utils.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,12 @@ public static JsonAssertionException composeError(JsonValue jv, String message)
9595
// Use to compose an exception when casting to an incorrect type
9696
public static JsonAssertionException composeTypeError(JsonValue jv, String expected) {
9797
var actual = switch (jv) {
98-
case JsonObject ignored -> "JsonObject";
99-
case JsonArray ignored -> "JsonArray";
100-
case JsonBoolean ignored -> "JsonBoolean";
101-
case JsonNull ignored -> "JsonNull";
102-
case JsonNumber ignored -> "JsonNumber";
103-
case JsonString ignored -> "JsonString";
98+
case JsonObject v -> "JsonObject";
99+
case JsonArray v -> "JsonArray";
100+
case JsonBoolean v -> "JsonBoolean";
101+
case JsonNull v -> "JsonNull";
102+
case JsonNumber v -> "JsonNumber";
103+
case JsonString v -> "JsonString";
104104
};
105105
return composeError(jv, "%s is not a %s.".formatted(actual, expected));
106106
}

json-java21/src/main/java/jdk/sandbox/java/util/json/Json.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.Map;
3333
import java.util.Objects;
3434
import java.util.stream.Collectors;
35-
3635
import jdk.sandbox.internal.util.json.JsonParser;
3736
import jdk.sandbox.internal.util.json.Utils;
3837

json-java21/src/main/java/jdk/sandbox/java/util/json/JsonArray.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.util.stream.Collectors;
3232

3333
import jdk.sandbox.internal.util.json.JsonArrayImpl;
34-
3534
/**
3635
* The interface that represents JSON array.
3736
* <p>
@@ -45,12 +44,22 @@
4544
public non-sealed interface JsonArray extends JsonValue {
4645

4746
/**
48-
* {@return an unmodifiable list of the {@code JsonValue} elements in
49-
* this {@code JsonArray}}
47+
* {@inheritDoc}
5048
*/
5149
@Override
5250
List<JsonValue> elements();
5351

52+
/**
53+
* {@inheritDoc}
54+
*
55+
* @param index {@inheritDoc}
56+
* @throws JsonAssertionException if the given index is out of bounds
57+
*/
58+
default JsonValue element(int index) {
59+
// Overridden to specify
60+
return JsonValue.super.element(index);
61+
}
62+
5463
/**
5564
* {@return the {@code JsonArray} created from the given
5665
* list of {@code JsonValue}s}

0 commit comments

Comments
 (0)