Skip to content

Fix #512: Add @JsonWrapped annotation for grouping scalar fields into nested JSON objects#5715

Open
sri-adarsh-kumar wants to merge 12 commits intoFasterXML:3.xfrom
sri-adarsh-kumar:feature/512-json-wrapped-annotation
Open

Fix #512: Add @JsonWrapped annotation for grouping scalar fields into nested JSON objects#5715
sri-adarsh-kumar wants to merge 12 commits intoFasterXML:3.xfrom
sri-adarsh-kumar:feature/512-json-wrapped-annotation

Conversation

@sri-adarsh-kumar
Copy link
Copy Markdown
Contributor

Summary

Closes #512. Introduces @JsonWrapped, a field-level annotation that groups multiple scalar properties under a single named wrapper object in the serialized JSON and reads them back during deserialization.

Usage:

class Gene {
    public String symbol;

    @JsonWrapped("chr")
    @JsonProperty("id")
    public String chrId;

    @JsonWrapped("chr")
    public String chrName;
}
// → {"symbol":"TP53","chr":{"id":"17","chrName":"chr17"}}

What's in the MVP

Serialization (grouping scalar fields into a named nested object) and deserialization (reading from the wrapper object back into flat fields), with full support for @JsonProperty on inner fields, multiple wrapper groups, non-contiguous fields, @JsonPropertyOrder, enabled=false, and round-trip correctness. Validation rejects non-scalar types, empty wrapper names, name conflicts, and combined use with @JsonUnwrapped.

What's NOT in the MVP

@JsonView on inner wrapped fields is ignored — the wrapper always emits all its fields regardless of active view. Class-level @JsonFilter applies to the wrapper property itself (by wrapper name) but not to individual inner fields, and class-level @JsonInclude (e.g. NON_NULL) still applies to inner fields during serialization.


Please add the gen-ai label to this PR.

@sri-adarsh-kumar
Copy link
Copy Markdown
Contributor Author

Submitting this for review.
Open for suggestions on what belongs or does not belong in the MVP.

@github-actions
Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.14% 📉 -0.060%
Branches branches 74.23% 📉 -0.060%

Coverage data generated from JaCoCo test results

1 similar comment
@github-actions
Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.14% 📉 -0.060%
Branches branches 74.23% 📉 -0.060%

Coverage data generated from JaCoCo test results

@sri-adarsh-kumar sri-adarsh-kumar marked this pull request as ready for review February 26, 2026 18:59
@@ -0,0 +1,75 @@
package tools.jackson.databind.annotation;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since there's nothing databind-specific, this belongs in jackson-annotations, but for now can keep it here while considering rest of implementation.
(only annotations that depend on something in databind should be added within it)

Comment thread src/main/java/tools/jackson/databind/annotation/JsonWrapped.java Outdated
@cowtowncoder cowtowncoder added the gen-ai PR created with Generative AI (whole or assisted) (or issue for which gen-ai seems suitable) label Feb 27, 2026
* <li>The wrapper name ({@code value()}) must be non-empty.</li>
* <li>The wrapper name must not conflict with an existing non-wrapped property on the same bean.</li>
* <li>Not supported on {@code @JsonCreator} constructor or factory-method parameters.</li>
* <li>MVP limitation: {@code @JsonView} on inner wrapped fields is ignored — the wrapper
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Views are tricky here, understood.

Set<SettableBeanProperty> wrappedPropSet = new HashSet<>();
for (SettableBeanProperty prop : allProps) {
AnnotatedMember member = prop.getMember();
if (member == null) continue;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

style: always use curly braces:

if (member == null) {
    continue;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed.
Are there any format check plugins or equivalent for this repo?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unfortunately no.

Comment thread src/main/java/tools/jackson/databind/deser/impl/WrappedPropertyHandler.java Outdated
Comment thread src/main/java/tools/jackson/databind/deser/impl/WrappedPropertyHandler.java Outdated
Comment thread src/main/java/tools/jackson/databind/deser/impl/WrappedPropertyHandler.java Outdated

@Override
public String findWrappedGroupName(MapperConfig<?> config, AnnotatedMember member) {
String r = _primary.findWrappedGroupName(config, member);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will not work properly with case of enabled = false, I think.

*
* @since 3.1
*/
public static boolean isScalarType(JavaType type) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am not sure why we'd want to limit handling to scalar types, so probably need not comment on implementation.

Comment thread src/test/java/tools/jackson/databind/struct/JsonWrappedDeserializationTest.java Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 1, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.15% 📉 -0.060%
Branches branches 74.22% 📉 -0.080%

Coverage data generated from JaCoCo test results

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 1, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.16% 📉 -0.050%
Branches branches 74.21% 📉 -0.090%

Coverage data generated from JaCoCo test results

1 similar comment
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 1, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.16% 📉 -0.050%
Branches branches 74.21% 📉 -0.090%

Coverage data generated from JaCoCo test results

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 1, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.15% 📉 -0.060%
Branches branches 74.25% 📉 -0.050%

Coverage data generated from JaCoCo test results

sri-adarsh-kumar and others added 2 commits March 1, 2026 20:00
…ntract and validation

Cleaned up the @JsonWrapped annotation implementation to use a three-value return contract
for the wrapper name: null (not present), empty string (explicitly disabled in mix-ins),
or non-empty string (active wrapper). Removed the redundant 'enabled' flag and simplified
validation logic in BeanDeserializerBase. Updated WrappedPropertyHandler method signatures.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ections, maps, arrays

Removes the scalar-only validation guards from BeanSerializerFactory and
BeanDeserializerBase that blocked non-scalar types. The wrapper contract is
unchanged — inner fields delegate to their own serializers/deserializers, so
any Jackson-serializable type works without new machinery. Updates Javadoc,
README, and converts former rejection tests to positive tests; adds
serialization, deserialization, and round-trip coverage for POJO, List, Map,
Array, mixed-type, and nested-wrapping scenarios.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sri-adarsh-kumar sri-adarsh-kumar force-pushed the feature/512-json-wrapped-annotation branch from fe5783b to 40393a0 Compare March 1, 2026 19:01
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 1, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.15% 📉 -0.060%
Branches branches 74.25% 📉 -0.050%

Coverage data generated from JaCoCo test results

1 similar comment
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 1, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.15% 📉 -0.060%
Branches branches 74.25% 📉 -0.050%

Coverage data generated from JaCoCo test results

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 1, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.16% 📉 -0.050%
Branches branches 74.25% 📉 -0.050%

Coverage data generated from JaCoCo test results

@github-actions
Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.29% 📉 -0.050%
Branches branches 74.50% 📉 -0.050%

Coverage data generated from JaCoCo test results

@github-actions
Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.30% 📉 -0.060%
Branches branches 74.56% 📉 -0.050%

Coverage data generated from JaCoCo test results

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 2, 2026

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.39% 📉 -0.050%
Branches branches 74.62% 📉 -0.050%

Coverage data generated from JaCoCo test results

@github-actions
Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.51% 📉 -0.060%
Branches branches 74.81% 📉 -0.060%

Coverage data generated from JaCoCo test results

@sri-adarsh-kumar
Copy link
Copy Markdown
Contributor Author

@cowtowncoder Small Reminder.
Please let me know if you need any changes from my side.

@github-actions
Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 81.53% 📉 -0.050%
Branches branches 74.81% 📉 -0.060%

Coverage data generated from JaCoCo test results

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

Labels

gen-ai PR created with Generative AI (whole or assisted) (or issue for which gen-ai seems suitable)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add @JsonWrapped

2 participants