Skip to content

feat(jackson3): add validated document reader with pointer diagnostics - #27

Merged
kazemek merged 2 commits into
mainfrom
feat/jackson3-document-reader
Jul 31, 2026
Merged

kazemek merged 2 commits into
mainfrom
feat/jackson3-document-reader

Conversation

@kazemek

@kazemek kazemek commented Jul 31, 2026 •

Copy link
Copy Markdown
Collaborator

Decode JSON:API into core documents via explicit PrimaryDataKind, then aggregate-validate before return. Retain token locations by pointer so local and aggregate failures report exact or nearest enclosing source positions.

Summary by CodeRabbit

  • New Features
    • Added Jackson 3 JSON:API document reading from strings, byte arrays, streams, and parsers.
    • Added explicit support for resource and resource-identifier primary data.
    • Added categorized read errors with JSON Pointers and source locations.
    • Added configurable, immutable document read contexts.
  • Bug Fixes
    • Improved validation path accuracy and consistency, including escaped JSON Pointer segments.
  • Documentation
    • Updated project, conformance, roadmap, and module documentation to reflect document reading availability and architecture decisions.

Decode JSON:API into core documents via explicit PrimaryDataKind, then
aggregate-validate before return. Retain token locations by pointer so
local and aggregate failures report exact or nearest enclosing source
positions.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ff5676d6-01bb-43aa-b980-aa01d1bffa25

📥 Commits

Reviewing files that changed from the base of the PR and between 3636f1a and 3a7062b.

📒 Files selected for processing (12)
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Relationships.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReader.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiJackson3.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/SourceLocation.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/JsonPointerAccumulator.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/PointerEscapes.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ReadLocations.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireObjectMembers.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireTokens.java
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/DocumentReaderIsolationSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/DocumentReaderSpec.groovy
🚧 Files skipped from review as they are similar to previous changes (8)
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/JsonPointerAccumulator.java
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/DocumentReaderIsolationSpec.groovy
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireTokens.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/SourceLocation.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiJackson3.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ReadLocations.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReader.java

📝 Walkthrough

Walkthrough

Added Jackson 3 JSON:API document reading with explicit primary-data selection, token-driven decoding, validation, categorized diagnostics, source locations, reader factories, tests, and updated project documentation.

Changes

Jackson 3 document reader

Layer / File(s) Summary
JSON Pointer validation paths
jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/{internal,model,validation}/*
Uses JsonPointers.child for escaped validation paths and fixed /jsonapi and /data validation roots.
Reader contracts and factory
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/{CodecFailureCategory,DocumentReadContext,JsonApiDocumentReadException,PrimaryDataKind,SourceLocation,JsonApiJackson3}.java
Adds reader context, primary-data selection, source locations, categorized exceptions, and mapper or builder reader factories.
Token-driven wire decoding
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/*
Adds token-based readers for documents, resources, identifiers, relationships, links, errors, open values, duplicate members, and JSON Pointer locations.
Reader orchestration and diagnostics
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReader.java, jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/*
Adds string, byte-array, stream, and parser inputs; preserves caller-owned inputs; performs local and aggregate validation; and converts failures to categorized exceptions.
Reader tests and documentation
jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/*, jsonapi-java-jackson3/README.md, docs/*, README.md, .agentWork/milestones/*
Adds reader, diagnostics, ownership, isolation, and validation tests. Updates module documentation, conformance, roadmap, ADR indexing, and Phase 2.4 completion status.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant JsonApiDocumentReader
  participant JsonApiWireReader
  participant DocumentWireReader
  participant JsonApiDocumentValidator
  Caller->>JsonApiDocumentReader: readValue(input)
  JsonApiDocumentReader->>JsonApiWireReader: readDocument(parser, primaryDataKind, locations)
  JsonApiWireReader->>DocumentWireReader: decode document
  DocumentWireReader-->>JsonApiWireReader: JsonApiDocument
  JsonApiWireReader-->>JsonApiDocumentReader: JsonApiDocument
  JsonApiDocumentReader->>JsonApiDocumentValidator: validate(document, context)
  JsonApiDocumentValidator-->>JsonApiDocumentReader: validation result
  JsonApiDocumentReader-->>Caller: document or categorized exception
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a validated Jackson 3 document reader with JSON Pointer diagnostics.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/jackson3-document-reader

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Relationships.java (1)

139-148: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pass additionalCopy directly to requireNoCollisions.

Remove castRelationships. The method accepts Map<K, ?> and only reads keys, so the unchecked cast is unnecessary and introduces an incorrect non-null Relationship type.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Relationships.java`
around lines 139 - 148, Update the relationship-copy flow in Relationships to
pass additionalCopy directly to requireNoCollisions, relying on its Map<K, ?>
parameter. Remove the castRelationships helper and all references to it,
preserving the existing collision validation behavior without introducing a
non-null Relationship cast.
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java (1)

143-149: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Unreachable default-arm self-checks duplicated across five drafts.

Each draft declares a member-name Set and tests it in the default arm of the member switch. Every name in each set already has an explicit case, so the default arm can never see one of those names and the WireTokens.unexpected(...) throw is unreachable. The sets exist only to catch a future edit that adds a constant to the set without adding a case. The same five-line idiom, the set field, and the throw are copied five times.

Replace the runtime self-check with a compile-time or test-time guarantee. One option: drop the sets and the default-arm check, then add a Spock test per draft that feeds every reserved member name and asserts it is decoded rather than placed in additional. That removes the duplication and keeps the protection.

  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java#L143-L149: remove the DOCUMENT_MEMBERS.contains(name) check and the DOCUMENT_MEMBERS field at Lines 26-33; keep the additional pass-through.
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java#L188-L194: remove the JSONAPI_MEMBERS.contains(name) check and the JSONAPI_MEMBERS field at Lines 35-37.
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ErrorWireReader.java#L84-L90: remove the ERROR_MEMBERS.contains(name) check and the ERROR_MEMBERS field at Lines 19-28.
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ErrorWireReader.java#L132-L138: remove the ERROR_SOURCE_MEMBERS.contains(name) check and the ERROR_SOURCE_MEMBERS field at Lines 30-31.
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/LinkWireReader.java#L101-L107: remove the LINK_OBJECT_MEMBERS.contains(name) check and the LINK_OBJECT_MEMBERS field at Lines 19-27.

Add the covering tests under each module's src/test/groovy/ directory, mirroring the main package structure. Based on coding guidelines: "Use Groovy and Spock for tests under each module's src/test/groovy/ directory, mirroring the main package structure."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java`
around lines 143 - 149, Remove the unreachable reserved-member self-checks and
their set fields from DocumentWireReader in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java
at lines 143-149 and 188-194, ErrorWireReader in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ErrorWireReader.java
at lines 84-90 and 132-138, and LinkWireReader in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/LinkWireReader.java
at lines 101-107; retain each default arm’s additional-member pass-through. Add
Groovy/Spock coverage under each module’s src/test/groovy directory, mirroring
the main package structure, verifying every reserved member is decoded rather
than added to additional.

Source: Coding guidelines

jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireTokens.java (2)

64-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Align the immutability policy for open containers, and record why List.copyOf is unsafe here.

readOpenArray returns Collections.unmodifiableList(...), while readOpenObject returns the mutable LinkedHashMap to the caller. Make the policy consistent.

Keep Collections.unmodifiableList in readOpenArray. List.copyOf rejects null elements, so a valid document such as {"meta":{"a":[null]}} would throw a NullPointerException. Add a short comment so a later consistency refactor does not switch it to List.copyOf like readStringArray at Line 34.

♻️ Proposed clarification
     while (parser.nextToken() != JsonToken.END_ARRAY) {
       pointer.pushIndex(index);
       pointer.capture(parser);
       values.add(readOpenValue(parser, pointer));
       pointer.pop();
       index++;
     }
+    // Open arrays may contain JSON null; List.copyOf would reject null elements.
     return Collections.unmodifiableList(values);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireTokens.java`
around lines 64 - 84, Update readOpenObject to return an unmodifiable map,
matching readOpenArray while preserving null-valued members. Keep
Collections.unmodifiableList in readOpenArray and add a brief comment explaining
that List.copyOf must not replace it because it rejects valid null elements.

53-62: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard readNumber against invalid tokens and enum values.

Pass pointer to readNumber, reject non-numeric tokens with UNEXPECTED_TOKEN, and add a case null, default arm to prevent uncategorized failures when getNumberType() returns null or adds a new constant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireTokens.java`
around lines 53 - 62, Update WireTokens.readNumber to accept the parser pointer,
validate that the current token is numeric, and reject invalid tokens with
UNEXPECTED_TOKEN. Extend the getNumberType() switch with a case null, default
arm that also produces the established unexpected-token error, preserving the
existing numeric conversions.
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireObjectMembers.java (1)

18-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the handler consumption contract.

The loop depends on an invariant: each MemberHandler must consume the complete member value before it returns. If a handler leaves the parser inside a value, the next parser.nextToken() reads a token inside that value and requireFieldName reports a misleading UNEXPECTED_TOKEN at the wrong pointer. State this invariant in the javadoc so future readers keep it.

♻️ Proposed javadoc addition
   /**
    * Expects {`@link` JsonToken#START_OBJECT}, captures the current pointer location, then invokes
    * {`@code` handler} once per member with the parser positioned on that member's value token.
+   *
+   * <p>Each handler must consume the complete member value, including nested arrays and objects,
+   * so the parser rests on the last token of that value when the handler returns.
    */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireObjectMembers.java`
around lines 18 - 36, Update the Javadoc for WireObjectMembers.forEachMember to
state that each MemberHandler invocation must consume the complete member value
and return with the parser positioned after that value, before the loop advances
to the next member. Keep the existing parsing behavior unchanged.
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiJackson3.java (1)

56-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Pass base directly to JsonApiDocumentReader. The reader uses the mapper only to create parsers. JsonApiWireReader performs token-driven decoding, and JsonApiDocumentModule registers only a serializer. This avoids the unnecessary rebuild() and module registration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiJackson3.java`
around lines 56 - 61, Update the reader method in JsonApiJackson3 to pass the
validated base JsonMapper directly to JsonApiDocumentReader instead of calling
documentMapper(base); leave the existing null checks and read context wiring
unchanged.
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReader.java (1)

113-126: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Advance the parser for any completed prior root value.

readValue(JsonParser) supports sequential root values. After a scalar root value, the parser sits on a value token such as VALUE_STRING, not on END_OBJECT or END_ARRAY. Line 116 then keeps that stale token, and decoding starts on the previous value. Advance whenever the current token is not a structural start token.

♻️ Proposed condition
   private static void ensureCurrentToken(JsonParser parser) {
     JsonToken token = parser.currentToken();
-    // After a prior root value, Jackson leaves the parser on END_OBJECT/END_ARRAY.
-    if (token == null || token == JsonToken.END_OBJECT || token == JsonToken.END_ARRAY) {
+    // After a prior root value, the parser rests on that value's last token.
+    if (token != JsonToken.START_OBJECT && token != JsonToken.START_ARRAY) {
       token = parser.nextToken();
       if (token == null) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReader.java`
around lines 113 - 126, Update ensureCurrentToken to advance the parser whenever
currentToken() is not a structural start token, rather than only after
END_OBJECT or END_ARRAY. Preserve the existing null-token handling and
malformed-document exception, while allowing readValue(JsonParser) to correctly
move past completed scalar and composite root values.
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java (1)

90-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated RFC 6901 escaping in the internal package. Two classes define the same escape implementation. One shared helper prevents the two copies from diverging.

  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java#L90-L92: move escape and unescape into a shared internal pointer utility and call it from join.
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/JsonPointerAccumulator.java#L51-L53: delete the local escape and call the shared utility from push.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java`
around lines 90 - 92, Extract the duplicated RFC 6901 escaping logic into one
shared internal pointer utility. In
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java:90-92,
move both escape and unescape there and update join to use the utility; in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/JsonPointerAccumulator.java:51-53,
remove the local escape method and update push to call the shared utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ReadLocations.java`:
- Around line 23-32: Update ReadLocations.from to compare the unknown location
using TokenStreamLocation.NA.equals(location) rather than identity comparison,
and return SourceLocation.UNKNOWN when all four location coordinates are
unavailable. Keep the existing Jackson 3 accessors and normal coordinate
conversion unchanged for valid locations.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReadException.java`:
- Around line 15-19: Update JsonApiDocumentReadException so its sourceLocation
field is either excluded from serialization with transient or its SourceLocation
type is made serializable, ensuring serializing the exception no longer throws
NotSerializableException while preserving source-location behavior during normal
use.

---

Nitpick comments:
In
`@jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Relationships.java`:
- Around line 139-148: Update the relationship-copy flow in Relationships to
pass additionalCopy directly to requireNoCollisions, relying on its Map<K, ?>
parameter. Remove the castRelationships helper and all references to it,
preserving the existing collision validation behavior without introducing a
non-null Relationship cast.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java`:
- Around line 143-149: Remove the unreachable reserved-member self-checks and
their set fields from DocumentWireReader in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java
at lines 143-149 and 188-194, ErrorWireReader in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ErrorWireReader.java
at lines 84-90 and 132-138, and LinkWireReader in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/LinkWireReader.java
at lines 101-107; retain each default arm’s additional-member pass-through. Add
Groovy/Spock coverage under each module’s src/test/groovy directory, mirroring
the main package structure, verifying every reserved member is decoded rather
than added to additional.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java`:
- Around line 90-92: Extract the duplicated RFC 6901 escaping logic into one
shared internal pointer utility. In
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java:90-92,
move both escape and unescape there and update join to use the utility; in
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/JsonPointerAccumulator.java:51-53,
remove the local escape method and update push to call the shared utility.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireObjectMembers.java`:
- Around line 18-36: Update the Javadoc for WireObjectMembers.forEachMember to
state that each MemberHandler invocation must consume the complete member value
and return with the parser positioned after that value, before the loop advances
to the next member. Keep the existing parsing behavior unchanged.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireTokens.java`:
- Around line 64-84: Update readOpenObject to return an unmodifiable map,
matching readOpenArray while preserving null-valued members. Keep
Collections.unmodifiableList in readOpenArray and add a brief comment explaining
that List.copyOf must not replace it because it rejects valid null elements.
- Around line 53-62: Update WireTokens.readNumber to accept the parser pointer,
validate that the current token is numeric, and reject invalid tokens with
UNEXPECTED_TOKEN. Extend the getNumberType() switch with a case null, default
arm that also produces the established unexpected-token error, preserving the
existing numeric conversions.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReader.java`:
- Around line 113-126: Update ensureCurrentToken to advance the parser whenever
currentToken() is not a structural start token, rather than only after
END_OBJECT or END_ARRAY. Preserve the existing null-token handling and
malformed-document exception, while allowing readValue(JsonParser) to correctly
move past completed scalar and composite root values.

In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiJackson3.java`:
- Around line 56-61: Update the reader method in JsonApiJackson3 to pass the
validated base JsonMapper directly to JsonApiDocumentReader instead of calling
documentMapper(base); leave the existing null checks and read context wiring
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 97aa321b-45a7-4926-806a-1a5fbc8cd4c8

📥 Commits

Reviewing files that changed from the base of the PR and between 3a5d615 and 3636f1a.

📒 Files selected for processing (37)
  • .agentWork/milestones/README.md
  • .agentWork/milestones/phase-2-4-document-reads.md
  • README.md
  • docs/adr/README.md
  • docs/conformance.md
  • docs/vision.md
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/internal/AdditionalMembers.java
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/internal/OrderedMaps.java
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Attributes.java
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Links.java
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Meta.java
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Relationships.java
  • jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/validation/JsonApiDocumentValidator.java
  • jsonapi-java-jackson3/README.md
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/CodecFailureCategory.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/DocumentReadContext.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReadException.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDocumentReader.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiJackson3.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/PrimaryDataKind.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/SourceLocation.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DocumentWireReader.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ErrorWireReader.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/JsonApiWireReader.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/JsonPointerAccumulator.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/LinkWireReader.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MemberClassifier.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ReadLocationIndex.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ReadLocations.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ResourceWireReader.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ValidationPointers.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireObjectMembers.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/WireTokens.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/package-info.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/package-info.java
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/DocumentReaderIsolationSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/DocumentReaderSpec.groovy

Clear the ValidationPointers cast Sonar issue, harden read diagnostics
and parser sequencing, and apply the high-signal CodeRabbit cleanups.
@kazemek

kazemek commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 3a7062b with the PR review + Sonar follow-ups:

Actionable

  • ReadLocations.from: value-equals for TokenStreamLocation.NA + unknown-coords → SourceLocation.UNKNOWN
  • SourceLocation is now Serializable

Sonar

  • Removed the redundant cast in ValidationPointers.forCore (java:S1905)

Nitpicks applied

  • Dropped Relationships.castRelationships; pass additionalCopy directly to requireNoCollisions
  • ensureCurrentToken advances whenever the current token is not a structural start (with a sequenced-parser scalar leftover test)
  • JsonApiJackson3.reader uses the caller mapper as-is (no serializer module on the read path)
  • WireTokens.readNumber validates numeric tokens + null/default arm; open-array List.copyOf note kept
  • Documented WireObjectMembers handler consumption contract
  • Shared RFC 6901 escaping in PointerEscapes

Skipped (as planned)

  • Removing unreachable *_MEMBERS.contains default-arm self-checks / extra reserved-member suites (low value)
  • Docstring-coverage warning (not a project gate)

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant