|
1 | | -# JSON Experimental - JDK 21+ Backport |
| 1 | +# java.util.json Backport for JDK 21+ |
2 | 2 |
|
3 | | -This repository contains a backport of the experimental JSON API from the [jdk-sandbox project](https://github.com/openjdk/jdk-sandbox) to JDK 21 and later. |
| 3 | +Early access to the future `java.util.json` API - tracking OpenJDK sandbox development. |
4 | 4 |
|
5 | | -## Origin |
| 5 | +## Project Vision |
6 | 6 |
|
7 | | -This code is derived from the official OpenJDK sandbox repository at commit [d22dc2ba89789041c3908cdaafadc1dcf8882ebf](https://github.com/openjdk/jdk-sandbox/commit/d22dc2ba89789041c3908cdaafadc1dcf8882ebf) ("Improve hash code spec wording"). |
| 7 | +This project provides Java developers with early access to the future `java.util.json` API patterns today, allowing code written against this API to migrate seamlessly when the official API is released. Rather than adopting third-party JSON libraries that will never align with future JDK standards, developers can start using tomorrow's API patterns today. |
| 8 | + |
| 9 | +## Current Status |
| 10 | + |
| 11 | +This code is derived from the official OpenJDK sandbox repository at commit [d22dc2ba89789041c3908cdaafadc1dcf8882ebf](https://github.com/openjdk/jdk-sandbox/commit/d22dc2ba89789041c3908cdaafadc1dcf8882ebf) (3 days ago - "Improve hash code spec wording"). |
8 | 12 |
|
9 | 13 | 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) |
10 | 14 |
|
| 15 | +## Project Goals |
| 16 | + |
| 17 | +- **Enable early adoption**: Let developers use future Java JSON patterns today on JDK 21+ |
| 18 | +- **Smooth migration path**: Code written against this API should require minimal changes when migrating to the official release |
| 19 | +- **API compatibility over performance**: Focus on matching the API design rather than competing with existing JSON libraries on speed |
| 20 | + |
| 21 | +## Non-Goals |
| 22 | + |
| 23 | +- **Performance competition**: This is not intended to be the fastest JSON library |
| 24 | +- **Feature additions**: No features beyond what's in the official sandbox/preview |
| 25 | +- **Production optimization**: The official implementation will have JVM-level optimizations unavailable to a backport |
| 26 | +- **API stability**: This backport may evolve as the official specification develops (if folks find it useful) |
| 27 | + |
11 | 28 | ## Modifications |
12 | 29 |
|
13 | 30 | This is a simplified backport with the following changes from the original: |
@@ -79,4 +96,128 @@ The conversion mappings are: |
79 | 96 | This is useful for: |
80 | 97 | - Integrating with existing code that uses standard collections |
81 | 98 | - Serializing/deserializing to formats that expect Java types |
82 | | -- Working with frameworks that use reflection on standard types |
| 99 | +- Working with frameworks that use reflection on standard types |
| 100 | + |
| 101 | +## Usage Examples |
| 102 | + |
| 103 | +### Record Mapping |
| 104 | + |
| 105 | +The most powerful feature is mapping between Java records and JSON: |
| 106 | + |
| 107 | +```java |
| 108 | +// Domain model using records |
| 109 | +record User(String name, String email, boolean active) {} |
| 110 | +record Team(String teamName, List<User> members) {} |
| 111 | + |
| 112 | +// Create a team with users |
| 113 | +Team team = new Team("Engineering", List.of( |
| 114 | + new User( "Alice", "[email protected]", true), |
| 115 | + new User( "Bob", "[email protected]", false) |
| 116 | +)); |
| 117 | + |
| 118 | +// Convert records to JSON |
| 119 | +JsonValue teamJson = Json.fromUntyped(Map.of( |
| 120 | + "teamName", team.teamName(), |
| 121 | + "members", team.members().stream() |
| 122 | + .map(u -> Map.of( |
| 123 | + "name", u.name(), |
| 124 | + "email", u.email(), |
| 125 | + "active", u.active() |
| 126 | + )) |
| 127 | + .toList() |
| 128 | +)); |
| 129 | + |
| 130 | +// Parse JSON back to records |
| 131 | +JsonObject parsed = (JsonObject) Json.parse(teamJson.toString()); |
| 132 | +Team reconstructed = new Team( |
| 133 | + ((JsonString) parsed.members().get("teamName")).value(), |
| 134 | + ((JsonArray) parsed.members().get("members")).values().stream() |
| 135 | + .map(v -> { |
| 136 | + JsonObject member = (JsonObject) v; |
| 137 | + return new User( |
| 138 | + ((JsonString) member.members().get("name")).value(), |
| 139 | + ((JsonString) member.members().get("email")).value(), |
| 140 | + ((JsonBoolean) member.members().get("active")).value() |
| 141 | + ); |
| 142 | + }) |
| 143 | + .toList() |
| 144 | +); |
| 145 | +``` |
| 146 | + |
| 147 | +### Building Complex JSON |
| 148 | + |
| 149 | +Create structured JSON programmatically: |
| 150 | + |
| 151 | +```java |
| 152 | +// Building a REST API response |
| 153 | +JsonObject response = JsonObject.of(Map.of( |
| 154 | + "status", JsonString.of("success"), |
| 155 | + "data", JsonObject.of(Map.of( |
| 156 | + "user", JsonObject.of(Map.of( |
| 157 | + "id", JsonNumber.of(12345), |
| 158 | + "name", JsonString.of("John Doe"), |
| 159 | + "roles", JsonArray.of(List.of( |
| 160 | + JsonString.of("admin"), |
| 161 | + JsonString.of("user") |
| 162 | + )) |
| 163 | + )), |
| 164 | + "timestamp", JsonNumber.of(System.currentTimeMillis()) |
| 165 | + )), |
| 166 | + "errors", JsonArray.of(List.of()) |
| 167 | +)); |
| 168 | +``` |
| 169 | + |
| 170 | +### Stream Processing |
| 171 | + |
| 172 | +Process JSON arrays efficiently with Java streams: |
| 173 | + |
| 174 | +```java |
| 175 | +// Filter active users from a JSON array |
| 176 | +JsonArray users = (JsonArray) Json.parse(jsonArrayString); |
| 177 | +List<String> activeUserEmails = users.values().stream() |
| 178 | + .map(v -> (JsonObject) v) |
| 179 | + .filter(obj -> ((JsonBoolean) obj.members().get("active")).value()) |
| 180 | + .map(obj -> ((JsonString) obj.members().get("email")).value()) |
| 181 | + .toList(); |
| 182 | +``` |
| 183 | + |
| 184 | +### Error Handling |
| 185 | + |
| 186 | +Handle parsing errors gracefully: |
| 187 | + |
| 188 | +```java |
| 189 | +try { |
| 190 | + JsonValue value = Json.parse(userInput); |
| 191 | + // Process valid JSON |
| 192 | +} catch (JsonParseException e) { |
| 193 | + // Handle malformed JSON with line/column information |
| 194 | + System.err.println("Invalid JSON at line " + e.getLine() + |
| 195 | + ", column " + e.getColumn() + ": " + e.getMessage()); |
| 196 | +} |
| 197 | +``` |
| 198 | + |
| 199 | +### Pretty Printing |
| 200 | + |
| 201 | +Format JSON for display: |
| 202 | + |
| 203 | +```java |
| 204 | +JsonObject data = JsonObject.of(Map.of( |
| 205 | + "name", JsonString.of("Alice"), |
| 206 | + "scores", JsonArray.of(List.of( |
| 207 | + JsonNumber.of(85), |
| 208 | + JsonNumber.of(90), |
| 209 | + JsonNumber.of(95) |
| 210 | + )) |
| 211 | +)); |
| 212 | + |
| 213 | +String formatted = Json.toDisplayString(data, 2); |
| 214 | +// Output: |
| 215 | +// { |
| 216 | +// "name": "Alice", |
| 217 | +// "scores": [ |
| 218 | +// 85, |
| 219 | +// 90, |
| 220 | +// 95 |
| 221 | +// ] |
| 222 | +// } |
| 223 | +``` |
0 commit comments