Skip to content

Fix #6397: Preserve buffered properties in immutable creator-based builders - #6407

Merged
cowtowncoder merged 9 commits into
FasterXML:3.xfrom
wozhenbang2004:fix/immutable-builder-buffered-properties
Nov 10, 2026
Merged

cowtowncoder merged 9 commits into
FasterXML:3.xfrom
wozhenbang2004:fix/immutable-builder-buffered-properties

Conversation

@wozhenbang2004

@wozhenbang2004 wozhenbang2004 commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #6397.

An immutable builder can return a replacement instance from a setter. When the value is read before the creator is invoked, applying the buffered assignment currently drops that returned instance.

Carry the replacement forward for buffered regular properties on the builder path. Ordinary bean assignment and buffered any-setter, merge, and map entries keep their current behavior.

The regression covers array-shaped and object-shaped input and verifies that a mutable bean ignores a setter return value. The two immutable-builder cases fail on the unmodified implementation.

Maven clean verify on JDK 21 completed with 7,775 tests passed and 1 skipped after merging the current 3.x base.

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.10% 📈 +0.010%
Branches branches 77.31% 📈 +0.020%

Coverage data generated from JaCoCo test results

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.11% 📈 +0.010%
Branches branches 77.33% 📈 +0.000%

Coverage data generated from JaCoCo test results

1 similar comment
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.11% 📈 +0.010%
Branches branches 77.33% 📈 +0.000%

Coverage data generated from JaCoCo test results

@cowtowncoder

Copy link
Copy Markdown
Member

Something wrong with this pr... ?

@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.13% 📈 +0.010%
Branches branches 77.34% 📈 +0.020%

Coverage data generated from JaCoCo test results

@cowtowncoder cowtowncoder added the cla-received PR already covered by CLA (optional label) label Nov 7, 2026
@cowtowncoder cowtowncoder changed the title Preserve buffered properties in immutable creator-based builders Fix #6397: Preserve buffered properties in immutable creator-based builders Nov 7, 2026
Comment thread release-notes/CREDITS Resolved
@cowtowncoder

Copy link
Copy Markdown
Member

@wozhenbang2004 Could you please enable "Allow edits from maintainers" on this PR? I'd like to push a small update directly: 3.x is now 3.4.0-SNAPSHOT (3.3.0 was released), so the #6397 release note entry needs to move into a new 3.4.0 section (and [3.4.0] in CREDITS) after merging 3.x into this branch.

Thanks!

@cowtowncoder

Copy link
Copy Markdown
Member

Suggestion for a small refactoring to avoid the instanceof check and cast in PropertyBasedCreator.build(): add assignAndReturn() to the base PropertyValue class with a default implementation, and override it in Regular:

// PropertyValue (base)
/**
 * Method called to assign stored value to given instance, returning the
 * instance to use from then on: for (immutable) Builders this may differ
 * from the one passed in. Default implementation calls {@link #assign} and
 * returns the original instance.
 *
 * @since 3.4
 */
public Object assignAndReturn(DeserializationContext ctxt, Object bean) throws JacksonException {
    assign(ctxt, bean);
    return bean;
}

// PropertyValue.Regular
@Override
public Object assignAndReturn(DeserializationContext ctxt, Object bean) throws JacksonException {
    return _property.setAndReturn(ctxt, bean, value);
}

and then in PropertyBasedCreator.build():

for (PropertyValue pv = buffer.buffered(); pv != null; pv = pv.next) {
    if (buffer._mayRebind) {
        bean = pv.assignAndReturn(ctxt, bean);
    } else {
        pv.assign(ctxt, bean);
    }
}

The _mayRebind check still needs to stay (as ignoresSetterReturnValueForOrdinaryBean verifies, a fluent setter on a regular bean must not replace the bean), but the branching is then on builder-vs-bean rather than on the concrete PropertyValue subtype. Behavior of Any, Map and AnyParameter is unchanged since they use the default.

@wozhenbang2004

Copy link
Copy Markdown
Contributor Author

@cowtowncoder Maintainer edits are enabled now. Thanks for taking care of the update!

@github-actions

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.18% 📈 +0.020%
Branches branches 77.44% 📈 +0.020%

Coverage data generated from JaCoCo test results

@cowtowncoder cowtowncoder added this to the 3.4.0 milestone Nov 10, 2026
- Remove VERSION/CREDITS entries: 3.3.0 is already released and this
  fix targets 3.x (3.4.0, no release-note entries yet)
- Make ImmutableBuilderWithCreator6397Test extend DatabindTestUtil
- Cover all creator.build() paths: creator property last or missing
  (END_OBJECT), short array (END_ARRAY), and @JsonUnwrapped builder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.18% 📈 +0.020%
Branches branches 77.44% 📈 +0.020%

Coverage data generated from JaCoCo test results

- Add `PropertyBasedCreator.build(ctxt, buffer, useSetterReturn)`;
  builder deserializers pass `true`, the 2-arg variant keeps ignoring
  setter return values. No longer depends on `PropertyValueBuffer._mayRebind`
  (an Object Id concern, [databind#5909]), so buffers created via the
  deprecated `startBuilding()` overloads are handled too
- Replace `instanceof PropertyValue.Regular` check with overridable
  `PropertyValue.assignAndReturn()` (default: `assign()` + return bean)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Nov 10, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

🟡 Medium risk · Applying setter-return replacements during buffered deserialization could produce incorrect final objects if a builder setter returns an unexpected instance.

Fixes buffered regular property assignment on immutable creator-based builders so the replacement instance returned by a setter is carried forward instead of being dropped when the value is read before the creator runs. No issues found.

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@github-actions

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.18% 📈 +0.020%
Branches branches 77.44% 📈 +0.020%

Coverage data generated from JaCoCo test results

@cowtowncoder
cowtowncoder merged commit 0736200 into FasterXML:3.x Nov 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.4 cla-received PR already covered by CLA (optional label)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@JsonFormat(shape = ARRAY) builder with @JsonCreator silently loses buffered property if builder is immutable

2 participants