Skip to content

Commit 26c4721

Browse files
committed
Sync upstream commit 91a479d: Reduce StringBuilder allocation in toDisplayString
Upstream API changes: - JsonString.value() -> JsonString.string() - JsonNumber.toNumber() -> JsonNumber.toLong()/toDouble() - JsonBoolean.value() -> JsonBoolean.bool() - JsonArray.values() -> JsonArray.elements() - Json.fromUntyped()/toUntyped() removed - New navigation: JsonValue.get(String), element(int), getOrAbsent() Backporting: - Added LazyConstant.java polyfill (upstream switched from StableValue) - Added Utils.powExact() polyfill for Math.powExact(long, int) - Replaced unnamed variables _ with ignored (Java 21 compat) - Updated all modules to use new API Verify: mvnd verify (390 tests pass)
1 parent 05577c5 commit 26c4721

File tree

34 files changed

+1311
-1474
lines changed

34 files changed

+1311
-1474
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1+
# Eclipse/IDE files
2+
.project
3+
.classpath
4+
.settings/
15
json-compatibility-suite/.classpath
26
json-compatibility-suite/.project
37
json-compatibility-suite/.settings/
48
json-java21/.classpath
59
json-java21/.project
610
json-java21/.settings/
11+
json-java21-api-tracker/.classpath
12+
json-java21-api-tracker/.project
13+
json-java21-api-tracker/.settings/
14+
json-java21-jtd/.classpath
15+
json-java21-jtd/.project
16+
json-java21-jtd/.settings/
717
json-java21-schema/src/test/resources/draft4/
818
json-java21-schema/src/test/resources/json-schema-test-suite-data/
919

README.md

Lines changed: 72 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -41,72 +41,77 @@ JsonValue value = Json.parse(json);
4141

4242
// Access as map-like structure
4343
JsonObject obj = (JsonObject) value;
44-
String name = ((JsonString) obj.members().get("name")).value();
45-
int age = ((JsonNumber) obj.members().get("age")).intValue();
46-
boolean active = ((JsonBoolean) obj.members().get("active")).value();
44+
String name = ((JsonString) obj.members().get("name")).string();
45+
long age = ((JsonNumber) obj.members().get("age")).toLong();
46+
boolean active = ((JsonBoolean) obj.members().get("active")).bool();
4747
```
4848

4949
### Simple Record Mapping
5050

5151
```java
5252
// Define records for structured data
53-
record User(String name, int age, boolean active) {}
53+
record User(String name, long age, boolean active) {}
5454

5555
// Parse JSON directly to records
5656
String userJson = "{\"name\":\"Bob\",\"age\":25,\"active\":false}";
5757
JsonObject jsonObj = (JsonObject) Json.parse(userJson);
5858

5959
// Map to record
6060
User user = new User(
61-
((JsonString) jsonObj.members().get("name")).value(),
62-
((JsonNumber) jsonObj.members().get("age")).intValue(),
63-
((JsonBoolean) jsonObj.members().get("active")).value()
61+
((JsonString) jsonObj.members().get("name")).string(),
62+
((JsonNumber) jsonObj.members().get("age")).toLong(),
63+
((JsonBoolean) jsonObj.members().get("active")).bool()
6464
);
6565

66-
// Convert records back to JSON
67-
JsonValue backToJson = Json.fromUntyped(Map.of(
68-
"name", user.name(),
69-
"age", user.age(),
70-
"active", user.active()
66+
// Convert records back to JSON using typed factories
67+
JsonValue backToJson = JsonObject.of(Map.of(
68+
"name", JsonString.of(user.name()),
69+
"age", JsonNumber.of(user.age()),
70+
"active", JsonBoolean.of(user.active())
7171
));
7272

73-
// Covert back to a JSON string
73+
// Convert back to a JSON string
7474
String jsonString = backToJson.toString();
7575
```
7676

77-
### Converting from Java Objects to JSON (`fromUntyped`)
77+
### Building JSON Programmatically
7878

7979
```java
80-
// Convert standard Java collections to JsonValue
81-
Map<String, Object> data = Map.of(
82-
"name", "John",
83-
"age", 30,
84-
"scores", List.of(85, 92, 78)
85-
);
86-
JsonValue json = Json.fromUntyped(data);
80+
// Build JSON using typed factory methods
81+
JsonObject data = JsonObject.of(Map.of(
82+
"name", JsonString.of("John"),
83+
"age", JsonNumber.of(30),
84+
"scores", JsonArray.of(List.of(
85+
JsonNumber.of(85),
86+
JsonNumber.of(92),
87+
JsonNumber.of(78)
88+
))
89+
));
90+
String json = data.toString();
8791
```
8892

89-
### Converting from JSON to Java Objects (`toUntyped`)
93+
### Extracting Values from JSON
9094

9195
```java
92-
// Convert JsonValue back to standard Java types
96+
// Extract values from parsed JSON
9397
JsonValue parsed = Json.parse("{\"name\":\"John\",\"age\":30}");
94-
Object data = Json.toUntyped(parsed);
95-
// Returns a Map<String, Object> with standard Java types
96-
```
98+
JsonObject obj = (JsonObject) parsed;
9799

98-
The conversion mappings are:
99-
- `JsonObject``Map<String, Object>`
100-
- `JsonArray``List<Object>`
101-
- `JsonString``String`
102-
- `JsonNumber``Number` (Long, Double, BigInteger, or BigDecimal)
103-
- `JsonBoolean``Boolean`
104-
- `JsonNull``null`
100+
// Use the new type-safe accessor methods
101+
String name = obj.get("name").string(); // Returns "John"
102+
long age = obj.get("age").toLong(); // Returns 30L
103+
double ageDouble = obj.get("age").toDouble(); // Returns 30.0
104+
```
105105

106-
This is useful for:
107-
- Integrating with existing code that uses standard collections
108-
- Serializing/deserializing to formats that expect Java types
109-
- Working with frameworks that use reflection on standard types
106+
The accessor methods on `JsonValue`:
107+
- `string()` - Returns the String value (for JsonString)
108+
- `toLong()` - Returns the long value (for JsonNumber, if representable)
109+
- `toDouble()` - Returns the double value (for JsonNumber, if representable)
110+
- `bool()` - Returns the boolean value (for JsonBoolean)
111+
- `elements()` - Returns List<JsonValue> (for JsonArray)
112+
- `members()` - Returns Map<String, JsonValue> (for JsonObject)
113+
- `get(String name)` - Access JsonObject member by name
114+
- `element(int index)` - Access JsonArray element by index
110115

111116
### Realistic Record Mapping
112117

@@ -123,29 +128,29 @@ Team team = new Team("Engineering", List.of(
123128
new User("Bob", "bob@example.com", false)
124129
));
125130

126-
// Convert records to JSON
127-
JsonValue teamJson = Json.fromUntyped(Map.of(
128-
"teamName", team.teamName(),
129-
"members", team.members().stream()
130-
.map(u -> Map.of(
131-
"name", u.name(),
132-
"email", u.email(),
133-
"active", u.active()
134-
))
135-
.toList()
131+
// Convert records to JSON using typed factories
132+
JsonValue teamJson = JsonObject.of(Map.of(
133+
"teamName", JsonString.of(team.teamName()),
134+
"members", JsonArray.of(team.members().stream()
135+
.map(u -> JsonObject.of(Map.of(
136+
"name", JsonString.of(u.name()),
137+
"email", JsonString.of(u.email()),
138+
"active", JsonBoolean.of(u.active())
139+
)))
140+
.toList())
136141
));
137142

138143
// Parse JSON back to records
139144
JsonObject parsed = (JsonObject) Json.parse(teamJson.toString());
140145
Team reconstructed = new Team(
141-
((JsonString) parsed.members().get("teamName")).value(),
142-
((JsonArray) parsed.members().get("members")).values().stream()
146+
((JsonString) parsed.members().get("teamName")).string(),
147+
((JsonArray) parsed.members().get("members")).elements().stream()
143148
.map(v -> {
144149
JsonObject member = (JsonObject) v;
145150
return new User(
146-
((JsonString) member.members().get("name")).value(),
147-
((JsonString) member.members().get("email")).value(),
148-
((JsonBoolean) member.members().get("active")).value()
151+
((JsonString) member.members().get("name")).string(),
152+
((JsonString) member.members().get("email")).string(),
153+
((JsonBoolean) member.members().get("active")).bool()
149154
);
150155
})
151156
.toList()
@@ -182,10 +187,10 @@ Process JSON arrays efficiently with Java streams:
182187
```java
183188
// Filter active users from a JSON array
184189
JsonArray users = (JsonArray) Json.parse(jsonArrayString);
185-
List<String> activeUserEmails = users.values().stream()
190+
List<String> activeUserEmails = users.elements().stream()
186191
.map(v -> (JsonObject) v)
187-
.filter(obj -> ((JsonBoolean) obj.members().get("active")).value())
188-
.map(obj -> ((JsonString) obj.members().get("email")).value())
192+
.filter(obj -> ((JsonBoolean) obj.members().get("active")).bool())
193+
.map(obj -> ((JsonString) obj.members().get("email")).string())
189194
.toList();
190195
```
191196

@@ -263,7 +268,14 @@ mvn exec:java -pl json-compatibility-suite -Dexec.args="--json"
263268

264269
## Current Status
265270

266-
This code (as of 2025-09-04) is derived from the OpenJDK jdk-sandbox repository “json” branch at commit [a8e7de8b49e4e4178eb53c94ead2fa2846c30635](https://github.com/openjdk/jdk-sandbox/commit/a8e7de8b49e4e4178eb53c94ead2fa2846c30635) ("Produce path/col during path building", 2025-08-14 UTC).
271+
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:
272+
- `JsonString.value()``JsonString.string()`
273+
- `JsonNumber.toNumber()``JsonNumber.toLong()` / `JsonNumber.toDouble()`
274+
- `JsonBoolean.value()``JsonBoolean.bool()`
275+
- `JsonArray.values()``JsonArray.elements()`
276+
- `Json.fromUntyped()` and `Json.toUntyped()` have been removed
277+
- New accessor methods on `JsonValue`: `get(String)`, `element(int)`, `getOrAbsent(String)`, `valueOrNull()`
278+
- Internal implementation changed from `StableValue` to `LazyConstant`
267279

268280
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)
269281

@@ -276,8 +288,11 @@ A daily workflow runs an API comparison against the OpenJDK sandbox and prints a
276288
## Modifications
277289

278290
This is a simplified backport with the following changes from the original:
279-
- Replaced `StableValue.of()` with double-checked locking pattern.
291+
- Replaced `LazyConstant` with a package-local polyfill using double-checked locking pattern.
292+
- Added `Utils.powExact()` polyfill for `Math.powExact(long, int)` which is not available in Java 21.
293+
- Replaced unnamed variables `_` with `ignored` for Java 21 compatibility.
280294
- Removed `@ValueBased` annotations.
295+
- Removed `@PreviewFeature` annotations.
281296
- Compatible with JDK 21.
282297

283298
## Security Considerations

0 commit comments

Comments
 (0)