Skip to content

Fix updates of non-public records - #6389

Closed
wozhenbang2004 wants to merge 3 commits into
FasterXML:3.xfrom
wozhenbang2004:patch-1
Closed

wozhenbang2004 wants to merge 3 commits into
FasterXML:3.xfrom
wozhenbang2004:patch-1

Conversation

@wozhenbang2004

@wozhenbang2004 wozhenbang2004 commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Problem

On 3.3.0-SNAPSHOT, readerForUpdating() throws a DatabindException when partially updating a non-public record, even though the same mapper can deserialize that record normally. The update path reads the existing component values through their accessors to seed the record constructor. A public accessor declared on a non-public record class can still fail the reflective access check in BeanDeserializer.

For example, updating new PkgRec("1", "2") with {"a":"x"} when PkgRec is a package-private record PkgRec(String a, String b) should return PkgRec[a=x, b=2]. The update instead fails with an access error before it can construct the result.

Summary

  • Apply the configured access override when reading an inaccessible record accessor during a partial update.
  • Add a regression test for a non-public record with public-access overrides disabled. The test verifies that the missing component retains its original value.

This leaves creator-parameter mapping unchanged. #6384 edits nearby code in the same method.

Validation

Added a JUnit regression test. Maven tests have not been run locally.

Fixes #6385

Honor the mapper access override settings when reading existing record
components during an update.

See FasterXML#6385
Verify that a partial update preserves existing component values.

See FasterXML#6385
@cowtowncoder

cowtowncoder commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

@wozhenbang2004 Would it be possible to add description of the problem being fixed, in the description -- not just what was changed?
When there is no separate issue, PR description needs to serve that purpose.

EDIT: my bad, there is already #6385

@gitar-bot

gitar-bot Bot commented Nov 3, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 1 closed / 1 findings

🟡 Medium risk · Applying configured reflective access overrides to record accessors changes partial updates; misapplied accessibility could cause failures or permit unintended component access.

Fixes readerForUpdating() failing on non-public records by applying the configured access override when reading record accessors during partial updates, which resolves the AccessibleObject.canAccess() failure on Android. A regression test covers a package-private record with public-access overrides disabled, and no open issues remain.

✅ 1 closed
✅ Bug: AccessibleObject.canAccess() missing on Android; record updates fail

📄 src/main/java/tools/jackson/databind/deser/bean/BeanDeserializer.java:351-354
The new guard calls accessor.canAccess(existingRecord) for every record component on every update, even when the record is public. The Android AccessibleObject reference does not list canAccess (only isAccessible/setAccessible), while records themselves are supported from API 34 (Class.getRecordComponents). On Android this call would throw NoSuchMethodError. That is an Error, so catch (Exception e) does not catch it, and readerForUpdating breaks for all records, not just non-public ones. Jackson 3 explicitly supports Android SDK 34+, and this is the only canAccess use in src/main. Drop the canAccess pre-check and rely on ClassUtil.checkAndFixAccess, which already decides whether setAccessible is needed. Caching the fixed accessors (e.g. once per deserializer) would also avoid repeating reflection work on each update.

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

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

🧪 Code Coverage Report

Metric Coverage Change
Instructions coverage 83.06% 📈 +0.000%
Branches branches 77.23% 📈 +0.000%

Coverage data generated from JaCoCo test results

@cowtowncoder

Copy link
Copy Markdown
Member

Thank you @wozhenbang2004! Closing in favor of #6403, which includes your commits as-is (authorship retained) plus release notes and caching of Record accessors (resolved once per deserializer instead of on every update). I couldn't push to this branch directly since maintainer edits are not enabled for it.

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.

Record update with readerForUpdating() fails for non-public Record: accessor not made accessible

2 participants