Keep Builder.defaultUseWrapper() from modifying shared introspector - #913
Merged
cowtowncoder merged 8 commits intoSep 23, 2026
Merged
cowtowncoder merged 8 commits into
cowtowncoder merged 8 commits into
Conversation
cowtowncoder
approved these changes
Sep 23, 2026
Member
|
Changed a bit, will merge: thank you again @Sahana2524 ! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
defaultUseWrapper()on a rebuilt builder also reconfigures the original mapperThe builder applied the setting through
setDefaultUseWrapper()on an introspector instance it shares with the mapper it came from viarebuild()(or one it already built), somapper.rebuild().defaultUseWrapper(false).build()flips wrapping on the original too,XmlMapper.shared()included: types it has not cached yet are written unwrapped, and a wrapped document reads back as an empty list without any error.Fix
The builder now never modifies the introspector in place, but swaps in a re-configured copy (the way
nameForTextElement()already does for the factory):XmlAnnotationIntrospectorgets new mutant factorywithDefaultUseWrapper(boolean)(since 3.3); default implementation returnsthis, for introspectors without the setting. Implementations must extend (and return)AnnotationIntrospector.JacksonXmlAnnotationIntrospectorimplements it with a newprotectedcopy constructor,JacksonXmlAnnotationIntrospector(JacksonXmlAnnotationIntrospector src, boolean defaultUseWrapper), which delegates to theJacksonAnnotationIntrospectorcopy constructor added in Add copy constructor forJacksonAnnotationIntrospectorjackson-databind#6231 (so databind settings are copied as well).XmlAnnotationIntrospector.Pairimplements it by delegating to its primary and secondary introspectors, returningthisif neither changes, so pair structure is retained.XmlMapper.Builder.defaultUseWrapper()callswithDefaultUseWrapper()on its introspector, then verifies that no contained XML introspector still needs the change; builder is only modified once both succeed.JacksonXmlAnnotationIntrospector.setDefaultUseWrapper()is deprecated in favor ofwithDefaultUseWrapper().README.md: replaced stale reference to 2.xJacksonXmlModule.setDefaultUseWrapper()withXmlMapper.builder().defaultUseWrapper().Behavior changes
Setting is always applied ("last call wins").
defaultUseWrapper()used to do nothing if the builder's own flag already had the given value, even if the introspector had since been replaced with one using a different setting (e.g.defaultUseWrapper(false), thenannotationIntrospector(new JacksonXmlAnnotationIntrospector(true)), thendefaultUseWrapper(false)again). It now always applies the setting to the introspector.Fail fast where a copy cannot be made. Whenever an introspector would need a different setting,
defaultUseWrapper()now throwsIllegalStateExceptionfor:JacksonXmlAnnotationIntrospectororXmlAnnotationIntrospector.Pairthat does not overridewithDefaultUseWrapper()(checked withClassUtil.verifyMustOverride(), same asObjectMapper.copy()), instead of silently losing its type;AnnotationIntrospectorPair(at top level, or nested withinXmlAnnotationIntrospector.Pair) containing an XML introspector that would need the change: it cannot be re-configured without changing its structure (and it does not dispatch XML-specific methods anyway); the message points toXmlAnnotationIntrospector.Pair.Previously these cases "worked" by modifying the shared instance (or, with the builder flag unchanged, were silently ignored).
Tests
MapperCopyTest:Pairsub-class, and databind pair (top-level and nested) fail without modifying introspectors or builder.