feat(jackson3): add annotated domain-to-resource mapping - #31
Conversation
Map @JsonApiResource POJOs/records to resource objects via Jackson 3's logical property model. JsonApiMapper derives from caller's mapper via rebuild() and caches ResourceMapping by type + config identity.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe Jackson 3 module now maps annotated domain objects to JSON:API resources and documents. The change adds mapping APIs, identifier conversion, diagnostics, relationship linkage handling, mapping-definition caching, validation relocation, tests, and project documentation. ChangesDomain resource mapping
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant JsonApiResourceMapper
participant DomainResourceWriter
participant MappingDefinitionCache
participant ResourceObject
JsonApiResourceMapper->>DomainResourceWriter: map domain resource
DomainResourceWriter->>MappingDefinitionCache: resolve cached mapping
MappingDefinitionCache-->>DomainResourceWriter: return ResourceMapping
DomainResourceWriter->>ResourceObject: build resource object
ResourceObject-->>JsonApiResourceMapper: return mapped resource
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Comment.java (1)
1-8: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAnnotate
authoras a relationship.
Article.authoruses@JsonApiRelationship, butComment.authordoes not. Add the annotation so direct mapping classifiesauthoras a relationship rather than an attribute.🤖 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/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Comment.java` around lines 1 - 8, Update the Comment record’s author component to include `@JsonApiRelationship`, matching Article.author, so direct mapping classifies author as a relationship instead of an attribute.
🧹 Nitpick comments (2)
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DomainResourceWriter.java (2)
124-186: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign
@JsonApiResourcevalidation between to-one and to-many relationship paths.
extractToManyLinkageexplicitly callscheckResourceAnnotation(elementClass)(line 179) and raisesUNSUPPORTED_RELATIONSHIP_COLLECTION_TYPEwhen the element type lacks@JsonApiResource.extractToOneLinkagehas no equivalent explicit check; a missing@JsonApiResourceon a to-one target only surfaces indirectly throughextractIdentifier→cache.resolve→MISSING_RESOURCE_ANNOTATION. The same root misconfiguration produces two different diagnostics depending on relationship cardinality, which makes error handling and diagnostics-based tests inconsistent.Consider calling
checkResourceAnnotation(value.getClass())inextractToOneLinkagebefore delegating toextractIdentifier, so both cardinalities raise the same class of diagnostic for the same root cause.🤖 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/DomainResourceWriter.java` around lines 124 - 186, The to-one relationship path lacks the explicit resource-annotation validation used by to-many relationships. Update extractToOneLinkage to call checkResourceAnnotation on the unwrapped value’s class before extractIdentifier, while preserving the existing ResourceIdentifier and RelationshipData handling.
248-266: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueClassify custom
Iterableimplementations as to-many.Replace
hasRawClass(Iterable.class)withisTypeOrSubTypeOf(Iterable.class). Jackson can represent custom iterable types as non-collection-likeJavaTypeinstances.🤖 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/DomainResourceWriter.java` around lines 248 - 266, Update isToManyType in DomainResourceWriter to use isTypeOrSubTypeOf(Iterable.class) instead of hasRawClass(Iterable.class), so custom Iterable implementations are classified as to-many while preserving the existing array and collection-like checks.
🤖 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/README.md`:
- Around line 44-46: Update the prefixer IdentifierConverter example passed to
JsonApiJackson3.resourceMapper so it returns null for a null idValue before
applying the "urn:" prefix; preserve the existing prefixed conversion for
non-null identifiers.
In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DomainResourceWriter.java`:
- Around line 88-98: Update buildAttributes to detect Optional-valued properties
before inserting them into the attributes map, unwrap present values, and skip
entries whose Optional is empty so they are not serialized as null; preserve
existing conversion for non-Optional and present Optional values, and add an
assertion that subtitle is absent.
In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.java`:
- Around line 89-108: Update classifyProperties so a propertyDefinition with no
accessor is not silently skipped when it has an explicit role annotation;
resolve or detect its annotated role and raise the established MappingDiagnostic
for a missing accessor, including the property context. Preserve skipping only
for unannotated properties and keep normal MappingProperty classification
unchanged when an accessor exists.
- Around line 157-231: Update validateJsonApiName to reject JSON:API-reserved
member names "id" and "type" in addition to invalid names, including when
supplied through explicit annotations. Raise the appropriate mapping diagnostic
before mapping resolution completes, while preserving the existing role-specific
validation behavior.
In
`@jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMappingJacksonFeaturesSpec.groovy`:
- Around line 178-289: Update the relationship collection classification logic
exercised by MixedRelEntity so unsupported elements consistently produce
UNSUPPORTED_RELATIONSHIP_VALUE regardless of their position relative to a
ResourceIdentifier. Ensure validation examines the complete collection or
otherwise preserves the same diagnostic for both element orders, while retaining
valid linkage handling for collections containing null and ResourceIdentifier
values.
---
Outside diff comments:
In
`@jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Comment.java`:
- Around line 1-8: Update the Comment record’s author component to include
`@JsonApiRelationship`, matching Article.author, so direct mapping classifies
author as a relationship instead of an attribute.
---
Nitpick comments:
In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DomainResourceWriter.java`:
- Around line 124-186: The to-one relationship path lacks the explicit
resource-annotation validation used by to-many relationships. Update
extractToOneLinkage to call checkResourceAnnotation on the unwrapped value’s
class before extractIdentifier, while preserving the existing ResourceIdentifier
and RelationshipData handling.
- Around line 248-266: Update isToManyType in DomainResourceWriter to use
isTypeOrSubTypeOf(Iterable.class) instead of hasRawClass(Iterable.class), so
custom Iterable implementations are classified as to-many while preserving the
existing array and collection-like checks.
🪄 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: 52b7d48a-d79f-4de1-a0e4-1eacdc18e595
📒 Files selected for processing (55)
.gitignoreREADME.mddocs/conformance.mdjsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/internal/AdditionalMembers.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/internal/package-info.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Attributes.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/JsonApiDocument.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Links.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Meta.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Relationship.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/Relationships.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/ResourceIdentifier.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/model/ResourceObject.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/validation/JsonApiDocumentValidator.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/validation/MemberNames.javajsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/validation/package-info.javajsonapi-java-core/src/test/groovy/io/github/kazemek/jsonapi/core/validation/MemberNamesSpec.groovyjsonapi-java-jackson3/README.mdjsonapi-java-jackson3/build.gradle.ktsjsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/DocumentEnvelope.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/IdentifierConverter.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiJackson3.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiMappingException.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiResourceMapper.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/MappingDiagnostic.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/DomainResourceWriter.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionCache.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingProperty.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/PropertyRole.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/ResourceMapping.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/package-info.javajsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/package-info.javajsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/DomainResourceWriterDiagnosticsSpec.groovyjsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/IdentifierConversionSpec.groovyjsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMapperIsolationSpec.groovyjsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMapperSpec.groovyjsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMappingJacksonFeaturesSpec.groovyjsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Article.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithArray.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithFormattedTitle.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithOptional.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithOptionalId.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithOptionalRelationship.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithSet.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/BaseBlog.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/BlogWithJsonProperty.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Comment.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ConventionalId.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/CreatorBasedArticle.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ExtendedBlog.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/FormattedTitle.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Person.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Tag.javajsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/TitleSerializer.java
💤 Files with no reviewable changes (1)
- jsonapi-java-core/src/main/java/io/github/kazemek/jsonapi/core/validation/JsonApiDocumentValidator.java
Omit empty Optional attributes, fail early on missing accessors and reserved member names, and make mixed to-many diagnostics order-independent.
|
Addressed the actionable CodeRabbit findings in 29eb528: Fixed
Skipped (intentionally)
|
Keep Spotless from formatting Eclipse/IntelliJ bin trees that sit outside the Gradle build directory.
Reflect that annotated domain-to-resource mapping is delivered, not deferred.
Break up dense linkage and role-resolution logic into focused helpers so the mapping engine stays easy to follow without changing behavior.
Avoid rescanning property members for name overrides after role resolution, and mark Phase 2.2 complete in the milestone index.
|



Map @JsonApiResource POJOs/records to resource objects via Jackson 3's logical property model. JsonApiMapper derives from caller's mapper via rebuild() and caches ResourceMapping by type + config identity.
Summary by CodeRabbit