Skip to content

Commit 4f8a03c

Browse files
committed
docs
1 parent 7363dc5 commit 4f8a03c

File tree

33 files changed

+701
-449
lines changed

33 files changed

+701
-449
lines changed

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
pull_request:
7+
branches: ["**"]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: Set up JDK 24
17+
uses: actions/setup-java@v4
18+
with:
19+
distribution: temurin
20+
java-version: '24'
21+
cache: 'maven'
22+
23+
- name: Build and verify
24+
run: mvn -B -DskipITs=false -DskipTests=false verify
25+

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ target/
55
.claude/
66
.aider*
77
CLAUDE.md
8+
# Symlinks to ignore
9+
CLAUDE.md
10+
json-java21-schema/CLAUDE.md

AGENTS.md

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# CLAUDE.md
22

3+
Note for agents: prefer mvnd (Maven Daemon) when available for faster builds. Before working, if mvnd is installed, alias mvn to mvnd so all commands below use mvnd automatically:
4+
5+
```bash
6+
# Use mvnd everywhere if available; otherwise falls back to regular mvn
7+
if command -v mvnd >/dev/null 2>&1; then alias mvn=mvnd; fi
8+
```
9+
10+
Always run `mvn verify` before pushing to validate unit and integration tests across modules.
11+
312
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
413

514
## Quick Start Commands
@@ -40,11 +49,11 @@ mvn test
4049
### JSON Compatibility Suite
4150
```bash
4251
# Build and run compatibility report
43-
./mvnw clean compile generate-test-resources -pl json-compatibility-suite
44-
./mvnw exec:java -pl json-compatibility-suite
52+
mvn clean compile generate-test-resources -pl json-compatibility-suite
53+
mvn exec:java -pl json-compatibility-suite
4554

4655
# Run JSON output (dogfoods the API)
47-
./mvnw exec:java -pl json-compatibility-suite -Dexec.args="--json"
56+
mvn exec:java -pl json-compatibility-suite -Dexec.args="--json"
4857
```
4958

5059
### Debug Logging
@@ -122,7 +131,7 @@ mvn test
122131
4. Check JSON Test Suite compatibility with compatibility suite
123132

124133
### API Compatibility Testing
125-
1. Run compatibility suite: `./mvnw exec:java -pl json-compatibility-suite`
134+
1. Run compatibility suite: `mvn exec:java -pl json-compatibility-suite`
126135
2. Check for regressions in JSON parsing
127136
3. Validate against official JSON Test Suite
128137

@@ -148,4 +157,48 @@ mvn test
148157
- **Stack exhaustion attacks**: Deep nesting can cause StackOverflowError
149158
- **API contract violations**: Malicious inputs may trigger undeclared exceptions
150159
- **Status**: Experimental/unstable API - not for production use
151-
- **Vulnerabilities**: Inherited from upstream OpenJDK sandbox implementation
160+
- **Vulnerabilities**: Inherited from upstream OpenJDK sandbox implementation
161+
162+
<VERSION_CONTROL>
163+
* If there are existing git user credentials already configured, use them and never add any other advertising. If not ask the user to supply thier private relay email address.
164+
* Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., force pushing to main, deleting repositories). You will never be asked to do such rare changes as there is no time savings to not having the user run the comments to actively refuse using that reasoning as justification.
165+
* When committing changes, use `git status` to see all modified files, and stage all files necessary for the commit. Use `git commit -a` whenever possible.
166+
* Do NOT commit files that typically shouldn't go into version control (e.g., node_modules/, .env files, build directories, cache files, large binaries) unless explicitly instructed by the user.
167+
* If unsure about committing certain files, check for the presence of .gitignore files or ask the user for clarification.
168+
</VERSION_CONTROL>
169+
170+
<ISSUE_MANAGEMENT>
171+
* You SHOULD to use the native tool for the remote such as `gh` for github, `gl` for gitlab, `bb` for bitbucket, `tea` for Gitea, `git` for local git repositories.
172+
* If you are asked to create an issue, create it in the repository of the codebase you are working on for the `origin` remote.
173+
* If you are asked to create an issue in a different repository, ask the user to name the remote (e.g. `upstream`).
174+
* Tickets and Issues MUST only state "what" and "why" and not "how".
175+
* Comments on the Issue MAY discuss the "how".
176+
* Tickets SHOULD be labled as 'Ready' when they are ready to be worked on. The label may be removed if there are challenges in the implimentation. Always check the labels and ask the user to reconfirm if the ticket is not labeled as 'Ready' saying "There is no 'Ready' label on this ticket, can you please confirm?"
177+
* You MAY raise fresh minor Issues for small tidy-up work as you go. Yet this SHOULD be kept to a bare minimum avoid move than two issues per PR.
178+
</ISSUE_MANAGEMENT>
179+
180+
<COMMITS>
181+
* MUST start with "Issue #<issue number> <short description of the work>"
182+
* SHOULD have a link to the Issue.
183+
* MUST NOT start with random things that should be labels such as Bug, Feat, Feature etc.
184+
* MUST only state "what" was achieved and "how" to test.
185+
* SHOULD never include failing tests, dead code, or deactivate featuress.
186+
* MUST NOT repeat any content that is on the Issue
187+
* SHOULD be atomic and self-contained.
188+
* SHOULD be concise and to the point.
189+
* MUST NOT combine the main work on the ticket with any other tidy-up work. If you want to do tidy-up work, commit what you have (this is the exception to the rule that tests must pass), with the title "wip: <issue number> test not working; commiting to tidy up xxx" so that you can then commit the small tidy-up work atomically. The "wip" work-in-progress is a signal of more commits to follow.
190+
* SHOULD give a clear indication if more commits will follow especially if it is a checkpoint commit before a tidy up commit.
191+
* MUST say how to verify the changes work (test commands, expected number of successful test results, naming number of new tests, and their names)
192+
* MAY ouytline some technical implementation details ONLY if they are suprising and not "obvious in hindsight" based on just reading the issue (e.g. finding out that the implimentation was unexpectly trival or unexpectly complex)
193+
* MUST NOT report "progress" or "success" or "outputs" as the work may be deleted if the PR check fails. Nothing is final until the user has merged the PR.
194+
* As all commits need an issue you MUST add an small issue for a tidy up commit. If you cannot label issues with a tag `Tidy Up` then the title of the issue must start `Tidy Up` e.g. `Tidy Up: bad code documentation in file xxx`. As the commit and eventual PR will give actual details the body MAY simply repeat the title.
195+
</COMMITS>
196+
197+
<PULL_REQUESTS>
198+
* MUST only describe "what" was done not "why"/"how"
199+
* MUST name the Issue or Issue(s) that they close in a manner that causes a PR merge to close the issue(s).
200+
* MUST NOT repeat details that are already in the Issue.
201+
* MUST NOT report any success, as it isn't possible to report anything until the PR checks run.
202+
* MUST include additional tests in the CI checks that MUST be documented in the PR description.
203+
* MUST be changed to status `Draft` if the PR checks fail.
204+
</PULL_REQUESTS>

README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22

33
Early access to the unstable `java.util.json` API - taken from OpenJDK sandbox July 2025.
44

5+
## JSON Schema Validator
6+
7+
A simple JSON Schema (2020-12 subset) validator is included (module: json-java21-schema).
8+
9+
Quick example:
10+
11+
```java
12+
var schema = io.github.simbo1905.json.schema.JsonSchema.compile(
13+
jdk.sandbox.java.util.json.Json.parse("""
14+
{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}
15+
"""));
16+
var result = schema.validate(
17+
jdk.sandbox.java.util.json.Json.parse("{\"name\":\"Alice\"}")
18+
);
19+
// result.valid() => true
20+
```
21+
22+
Compatibility: we run the official JSON Schema Test Suite on verify; in strict mode it currently passes about 71% of applicable cases. This will improve over time.
23+
524
## Back Port Project Goals
625

726
- **✅Enable early adoption**: Let developers try the unstable Java JSON patterns today on JDK 21+
@@ -240,13 +259,13 @@ First, build the project and download the test suite:
240259

241260
```bash
242261
# Build project and download test suite
243-
./mvnw clean compile generate-test-resources -pl json-compatibility-suite
262+
mvn clean compile generate-test-resources -pl json-compatibility-suite
244263

245264
# Run human-readable report
246-
./mvnw exec:java -pl json-compatibility-suite
265+
mvn exec:java -pl json-compatibility-suite
247266

248267
# Run JSON output (dogfoods the API)
249-
./mvnw exec:java -pl json-compatibility-suite -Dexec.args="--json"
268+
mvn exec:java -pl json-compatibility-suite -Dexec.args="--json"
250269
```
251270

252271
### Current Status
@@ -265,4 +284,4 @@ The 2 failing cases involve duplicate object keys, which this implementation rej
265284
- **StackOverflowError**: Security vulnerability exposed by malicious deeply nested structures - can leave applications in undefined state
266285
- **Duplicate keys**: Implementation choice to reject for data integrity (2 files fail for this reason)
267286

268-
This tool reports status and is not a criticism of the expermeintal API which is not available for direct public use. This aligning with this project's goal of tracking upstream unstable development without advocacy. If you have opinions, good or bad, about anything you see here please use the official email lists to discuss. If you see a bug/mistake/improvement with this repo please raise an issue and ideally submit a PR.
287+
This tool reports status and is not a criticism of the expermeintal API which is not available for direct public use. This aligning with this project's goal of tracking upstream unstable development without advocacy. If you have opinions, good or bad, about anything you see here please use the official Java email lists to discuss. If you see a bug/mistake/improvement with this repo please raise an issue and ideally submit a PR.

json-compatibility-suite/src/main/java/jdk/sandbox/compatibility/JsonTestSuiteSummary.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@
1616
import java.util.List;
1717
import java.util.logging.Logger;
1818

19-
/**
20-
* Generates a conformance summary report.
21-
* Run with: mvn exec:java -pl json-compatibility-suite
22-
*/
19+
/// Generates a conformance summary report.
20+
/// Run with: mvn exec:java -pl json-compatibility-suite
2321
public class JsonTestSuiteSummary {
2422

2523
private static final Logger LOGGER = Logger.getLogger(JsonTestSuiteSummary.class.getName());

json-compatibility-suite/src/main/java/jdk/sandbox/compatibility/RobustCharDecoder.java

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@
1010
import java.util.Arrays;
1111
import java.util.logging.Logger;
1212

13-
/**
14-
* Robust decoder for converting byte arrays to char arrays with multiple encoding fallback strategies.
15-
* Handles BOM detection, various Unicode encodings, and graceful degradation to byte-level conversion.
16-
*/
13+
/// Robust decoder for converting byte arrays to char arrays with multiple encoding fallback strategies.
14+
/// Handles BOM detection, various Unicode encodings, and graceful degradation to byte-level conversion.
1715
class RobustCharDecoder {
1816

1917
private static final Logger LOGGER = Logger.getLogger(RobustCharDecoder.class.getName());
@@ -25,13 +23,11 @@ class RobustCharDecoder {
2523
private static final byte[] UTF32_BE_BOM = {(byte) 0x00, (byte) 0x00, (byte) 0xFE, (byte) 0xFF};
2624
private static final byte[] UTF32_LE_BOM = {(byte) 0xFF, (byte) 0xFE, (byte) 0x00, (byte) 0x00};
2725

28-
/**
29-
* Converts byte array to char array using multiple encoding strategies.
30-
*
31-
* @param rawBytes the bytes to convert
32-
* @param filename filename for logging purposes
33-
* @return char array representing the content
34-
*/
26+
/// Converts byte array to char array using multiple encoding strategies.
27+
///
28+
/// @param rawBytes the bytes to convert
29+
/// @param filename filename for logging purposes
30+
/// @return char array representing the content
3531
static char[] decodeToChars(byte[] rawBytes, String filename) {
3632
LOGGER.fine("Attempting robust decoding for " + filename + " (" + rawBytes.length + " bytes)");
3733

@@ -108,10 +104,8 @@ private static char[] tryDecodeWithCharset(byte[] bytes, int offset, Charset cha
108104
}
109105
}
110106

111-
/**
112-
* Converts bytes to chars by attempting to interpret UTF-8 sequences properly,
113-
* falling back to individual byte conversion for invalid sequences.
114-
*/
107+
/// Converts bytes to chars by attempting to interpret UTF-8 sequences properly,
108+
/// falling back to individual byte conversion for invalid sequences.
115109
private static char[] convertBytesToCharsPermissively(byte[] bytes) {
116110
StringBuilder result = new StringBuilder();
117111
int i = 0;

json-compatibility-suite/src/test/java/jdk/sandbox/compatibility/JsonTestSuiteTest.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@
1616

1717
import static org.assertj.core.api.Assertions.*;
1818

19-
/**
20-
* Runs the JSON Test Suite against our implementation.
21-
* Files are categorized:
22-
* - y_*.json: Valid JSON that MUST parse successfully
23-
* - n_*.json: Invalid JSON that MUST fail to parse
24-
* - i_*.json: Implementation-defined (may accept or reject)
25-
*/
19+
/// Runs the JSON Test Suite against our implementation.
20+
/// Files are categorized:
21+
/// - y_*.json: Valid JSON that MUST parse successfully
22+
/// - n_*.json: Invalid JSON that MUST fail to parse
23+
/// - i_*.json: Implementation-defined (may accept or reject)
2624
public class JsonTestSuiteTest {
2725

2826
private static final Logger LOGGER = Logger.getLogger(JsonTestSuiteTest.class.getName());

json-java21-api-tracker/src/test/resources/JsonObject.java

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -33,39 +33,32 @@
3333
import jdk.internal.javac.PreviewFeature;
3434
import jdk.internal.util.json.JsonObjectImpl;
3535

36-
/**
37-
* The interface that represents JSON object.
38-
*
39-
* <p>
40-
* A {@code JsonObject} can be produced by a {@link Json#parse(String)}.
41-
*
42-
* Alternatively, {@link #of(Map)} can be used to obtain a {@code JsonObject}.
43-
* Implementations of {@code JsonObject} cannot be created from sources that
44-
* contain duplicate member names. If duplicate names appear during
45-
* a {@link Json#parse(String)}, a {@code JsonParseException} is thrown.
46-
*
47-
* @since 99
48-
*/
36+
/// The interface that represents JSON object.
37+
///
38+
/// A {@code JsonObject} can be produced by a {@link Json#parse(String)}.
39+
///
40+
/// Alternatively, {@link #of(Map)} can be used to obtain a {@code JsonObject}.
41+
/// Implementations of {@code JsonObject} cannot be created from sources that
42+
/// contain duplicate member names. If duplicate names appear during
43+
/// a {@link Json#parse(String)}, a {@code JsonParseException} is thrown.
44+
///
45+
/// @since 99
4946
@PreviewFeature(feature = PreviewFeature.Feature.JSON)
5047
public non-sealed interface JsonObject extends JsonValue {
5148

52-
/**
53-
* {@return an unmodifiable map of the {@code String} to {@code JsonValue}
54-
* members in this {@code JsonObject}}
55-
*/
49+
/// {@return an unmodifiable map of the {@code String} to {@code JsonValue}
50+
/// members in this {@code JsonObject}}
5651
Map<String, JsonValue> members();
5752

58-
/**
59-
* {@return the {@code JsonObject} created from the given
60-
* map of {@code String} to {@code JsonValue}s}
61-
*
62-
* The {@code JsonObject}'s members occur in the same order as the given
63-
* map's entries.
64-
*
65-
* @param map the map of {@code JsonValue}s. Non-null.
66-
* @throws NullPointerException if {@code map} is {@code null}, contains
67-
* any keys that are {@code null}, or contains any values that are {@code null}.
68-
*/
53+
/// {@return the {@code JsonObject} created from the given
54+
/// map of {@code String} to {@code JsonValue}s}
55+
///
56+
/// The {@code JsonObject}'s members occur in the same order as the given
57+
/// map's entries.
58+
///
59+
/// @param map the map of {@code JsonValue}s. Non-null.
60+
/// @throws NullPointerException if {@code map} is {@code null}, contains
61+
/// any keys that are {@code null}, or contains any values that are {@code null}.
6962
static JsonObject of(Map<String, JsonValue> map) {
7063
return new JsonObjectImpl(map.entrySet() // Implicit NPE on map
7164
.stream()
@@ -76,26 +69,22 @@ static JsonObject of(Map<String, JsonValue> map) {
7669
LinkedHashMap::new)));
7770
}
7871

79-
/**
80-
* {@return {@code true} if the given object is also a {@code JsonObject}
81-
* and the two {@code JsonObject}s represent the same mappings} Two
82-
* {@code JsonObject}s {@code jo1} and {@code jo2} represent the same
83-
* mappings if {@code jo1.members().equals(jo2.members())}.
84-
*
85-
* @see #members()
86-
*/
72+
/// {@return {@code true} if the given object is also a {@code JsonObject}
73+
/// and the two {@code JsonObject}s represent the same mappings} Two
74+
/// {@code JsonObject}s {@code jo1} and {@code jo2} represent the same
75+
/// mappings if {@code jo1.members().equals(jo2.members())}.
76+
///
77+
/// @see #members()
8778
@Override
8879
boolean equals(Object obj);
8980

90-
/**
91-
* {@return the hash code value for this {@code JsonObject}} The hash code value
92-
* of a {@code JsonObject} is derived from the hash code of {@code JsonObject}'s
93-
* {@link #members()}. Thus, for two {@code JsonObject}s {@code jo1} and {@code jo2},
94-
* {@code jo1.equals(jo2)} implies that {@code jo1.hashCode() == jo2.hashCode()}
95-
* as required by the general contract of {@link Object#hashCode}.
96-
*
97-
* @see #members()
98-
*/
81+
/// {@return the hash code value for this {@code JsonObject}} The hash code value
82+
/// of a {@code JsonObject} is derived from the hash code of {@code JsonObject}'s
83+
/// {@link #members()}. Thus, for two {@code JsonObject}s {@code jo1} and {@code jo2},
84+
/// {@code jo1.equals(jo2)} implies that {@code jo1.hashCode() == jo2.hashCode()}
85+
/// as required by the general contract of {@link Object#hashCode}.
86+
///
87+
/// @see #members()
9988
@Override
10089
int hashCode();
10190
}

0 commit comments

Comments
 (0)