Skip to content

Commit b2f2dc8

Browse files
authored
JSON Schema validator: docs + verify integration (closes #19) (#18)
* json schema * all tests passing * more tests * compatibility suite tests
1 parent 84d41ca commit b2f2dc8

File tree

37 files changed

+4161
-384
lines changed

37 files changed

+4161
-384
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: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# CLAUDE.md
2+
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+
12+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
13+
14+
## Quick Start Commands
15+
16+
### Building the Project
17+
```bash
18+
# Full build
19+
mvn clean compile
20+
mvn package
21+
22+
# Build specific module
23+
mvn clean compile -pl json-java21
24+
mvn package -pl json-java21
25+
26+
# Build with test skipping
27+
mvn clean compile -DskipTests
28+
```
29+
30+
### Running Tests
31+
```bash
32+
# Run all tests
33+
mvn test
34+
35+
# Run tests with clean output (recommended)
36+
./mvn-test-no-boilerplate.sh
37+
38+
# Run specific test class
39+
./mvn-test-no-boilerplate.sh -Dtest=JsonParserTests
40+
./mvn-test-no-boilerplate.sh -Dtest=JsonTypedUntypedTests
41+
42+
# Run specific test method
43+
./mvn-test-no-boilerplate.sh -Dtest=JsonParserTests#testParseEmptyObject
44+
45+
# Run tests in specific module
46+
./mvn-test-no-boilerplate.sh -pl json-java21-api-tracker -Dtest=ApiTrackerTest
47+
```
48+
49+
### JSON Compatibility Suite
50+
```bash
51+
# Build and run compatibility report
52+
mvn clean compile generate-test-resources -pl json-compatibility-suite
53+
mvn exec:java -pl json-compatibility-suite
54+
55+
# Run JSON output (dogfoods the API)
56+
mvn exec:java -pl json-compatibility-suite -Dexec.args="--json"
57+
```
58+
59+
### Debug Logging
60+
```bash
61+
# Enable debug logging for specific test
62+
./mvn-test-no-boilerplate.sh -Dtest=JsonParserTests -Djava.util.logging.ConsoleHandler.level=FINER
63+
```
64+
65+
## Architecture Overview
66+
67+
### Module Structure
68+
- **`json-java21`**: Core JSON API implementation (main library)
69+
- **`json-java21-api-tracker`**: API evolution tracking utilities
70+
- **`json-compatibility-suite`**: JSON Test Suite compatibility validation
71+
72+
### Core Components
73+
74+
#### Public API (jdk.sandbox.java.util.json)
75+
- **`Json`** - Static utility class for parsing/formatting/conversion
76+
- **`JsonValue`** - Sealed root interface for all JSON types
77+
- **`JsonObject`** - JSON objects (key-value pairs)
78+
- **`JsonArray`** - JSON arrays
79+
- **`JsonString`** - JSON strings
80+
- **`JsonNumber`** - JSON numbers
81+
- **`JsonBoolean`** - JSON booleans
82+
- **`JsonNull`** - JSON null
83+
84+
#### Internal Implementation (jdk.sandbox.internal.util.json)
85+
- **`JsonParser`** - Recursive descent JSON parser
86+
- **`Json*Impl`** - Immutable implementations of JSON types
87+
- **`Utils`** - Internal utilities and factory methods
88+
89+
### Design Patterns
90+
- **Algebraic Data Types**: Sealed interfaces with exhaustive pattern matching
91+
- **Immutable Value Objects**: All types are immutable and thread-safe
92+
- **Lazy Evaluation**: Strings/numbers store offsets until accessed
93+
- **Factory Pattern**: Static factory methods for construction
94+
- **Bridge Pattern**: Clean API/implementation separation
95+
96+
## Key Development Practices
97+
98+
### Testing Approach
99+
- **JUnit 5** with AssertJ for fluent assertions
100+
- **Test Organization**:
101+
- `JsonParserTests` - Parser-specific tests
102+
- `JsonTypedUntypedTests` - Conversion tests
103+
- `JsonRecordMappingTests` - Record mapping tests
104+
- `ReadmeDemoTests` - Documentation example validation
105+
106+
### Code Style
107+
- **JEP 467 Documentation**: Use `///` triple-slash comments
108+
- **Immutable Design**: All public types are immutable
109+
- **Pattern Matching**: Use switch expressions with sealed types
110+
- **Null Safety**: Use `Objects.requireNonNull()` for public APIs
111+
112+
### Performance Considerations
113+
- **Lazy String/Number Creation**: Values computed on demand
114+
- **Singleton Patterns**: Single instances for true/false/null
115+
- **Defensive Copies**: Immutable collections prevent external modification
116+
- **Efficient Parsing**: Character array processing with minimal allocations
117+
118+
## Common Workflows
119+
120+
### Adding New JSON Type Support
121+
1. Add interface extending `JsonValue`
122+
2. Add implementation in `jdk.sandbox.internal.util.json`
123+
3. Update `Json.fromUntyped()` and `Json.toUntyped()`
124+
4. Add parser support in `JsonParser`
125+
5. Add comprehensive tests
126+
127+
### Debugging Parser Issues
128+
1. Enable `FINER` logging: `-Djava.util.logging.ConsoleHandler.level=FINER`
129+
2. Use `./mvn-test-no-boilerplate.sh` for clean output
130+
3. Focus on specific test: `-Dtest=JsonParserTests#testMethod`
131+
4. Check JSON Test Suite compatibility with compatibility suite
132+
133+
### API Compatibility Testing
134+
1. Run compatibility suite: `mvn exec:java -pl json-compatibility-suite`
135+
2. Check for regressions in JSON parsing
136+
3. Validate against official JSON Test Suite
137+
138+
## Module-Specific Details
139+
140+
### json-java21
141+
- **Main library** containing the core JSON API
142+
- **Maven coordinates**: `io.github.simbo1905.json:json-java21:0.1-SNAPSHOT`
143+
- **JDK requirement**: Java 21+
144+
145+
### json-compatibility-suite
146+
- **Downloads** JSON Test Suite from GitHub automatically
147+
- **Reports** 99.3% conformance with JSON standards
148+
- **Identifies** security vulnerabilities (StackOverflowError with deep nesting)
149+
- **Usage**: Educational/testing, not production-ready
150+
151+
### json-java21-api-tracker
152+
- **Tracks** API evolution and compatibility
153+
- **Uses** Java 24 preview features (`--enable-preview`)
154+
- **Purpose**: Monitor upstream OpenJDK changes
155+
156+
## Security Notes
157+
- **Stack exhaustion attacks**: Deep nesting can cause StackOverflowError
158+
- **API contract violations**: Malicious inputs may trigger undeclared exceptions
159+
- **Status**: Experimental/unstable API - not for production use
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/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<name>API Tracker</name>
1717

1818
<properties>
19-
<maven.compiler.release>24</maven.compiler.release>
19+
<maven.compiler.release>21</maven.compiler.release>
2020
</properties>
2121

2222
<dependencies>
@@ -54,4 +54,4 @@
5454
</plugin>
5555
</plugins>
5656
</build>
57-
</project>
57+
</project>

0 commit comments

Comments
 (0)