Background
Follow-up to #5222 — that thread has long history and conflicting ideas, so per @cowtowncoder's request this issue captures only the cleaned-up plan.
In #5222, @cowtowncoder wrote:
I think it'd be reasonable to change "mode" auto-detection/heuristics to consider single-Record-arg Constructor be considered delegating
This issue proposes exactly that, scoped tightly to keep regression risk low, and gated behind a new MapperFeature for opt-out.
Motivation
General single-argument Creator auto-detection is fundamentally ambiguous (properties vs delegating), which is why Jackson requires explicit @JsonCreator(mode = ...) today. Java record types resolve that ambiguity structurally:
- A
record has well-defined components with names and types
- A constructor
Foo(SomeRecord r) cannot reasonably mean "treat SomeRecord's components as named properties of Foo" — they belong to a different type
- The only consistent interpretation is DELEGATING: deserialize the JSON as
SomeRecord, then pass it to Foo's constructor
This makes record-argument Creators a safe, narrow case where auto-detection adds real value over requiring @JsonCreator(mode = DELEGATING).
Proposed change
Extend POJOPropertiesCollector._isDelegatingConstructor(PotentialCreator) (and the equivalent path for static factory methods) to also return true when ALL of the following hold:
- The Creator has exactly one parameter
- The parameter type
isRecord()
- The declaring class has no other Creator carrying an explicit
@JsonCreator annotation (so an explicit annotation always wins)
- The new
MapperFeature is enabled
When the heuristic fires, the Creator is treated as if it carried @JsonCreator(mode = DELEGATING).
New MapperFeature
MapperFeature.INFER_CREATOR_FROM_RECORD_ARGUMENT — default enabled.
Javadoc will state:
- What it does (one line)
- Why it's record-specific (component metadata removes the usual 1-arg properties/delegating ambiguity)
- That disabling restores pre-existing behavior exactly
Out of scope
Test plan
New behavior:
- record-arg constructor alone → DELEGATING
- record-arg static factory
static Foo of(Rec r) → DELEGATING
- record-arg constructor + no-arg constructor → record-arg wins for record-shaped JSON
Regression guards:
- Explicit
@JsonCreator(mode = PROPERTIES) on record-arg → PROPERTIES wins
- Any other
@JsonCreator present in the class → record-arg ignored
- Non-record single-arg Creator → unchanged (no auto-detect)
- Generic record parameter (
Box<T>) → unchanged from current
MapperFeature disabled → byte-for-byte old behavior
Existing tests under records/, DelegatingCreatorsTest, RecordExplicitCreatorsTest, DelegatingCreatorImplicitNamesTest must continue to pass unchanged.
Target
Jackson 3.x (branch 3.x) — behavioral change.
Happy to take this on if the plan looks good.
Background discussion: #5222
Background
Follow-up to #5222 — that thread has long history and conflicting ideas, so per @cowtowncoder's request this issue captures only the cleaned-up plan.
In #5222, @cowtowncoder wrote:
This issue proposes exactly that, scoped tightly to keep regression risk low, and gated behind a new
MapperFeaturefor opt-out.Motivation
General single-argument Creator auto-detection is fundamentally ambiguous (properties vs delegating), which is why Jackson requires explicit
@JsonCreator(mode = ...)today. Javarecordtypes resolve that ambiguity structurally:recordhas well-defined components with names and typesFoo(SomeRecord r)cannot reasonably mean "treatSomeRecord's components as named properties ofFoo" — they belong to a different typeSomeRecord, then pass it toFoo's constructorThis makes record-argument Creators a safe, narrow case where auto-detection adds real value over requiring
@JsonCreator(mode = DELEGATING).Proposed change
Extend
POJOPropertiesCollector._isDelegatingConstructor(PotentialCreator)(and the equivalent path for static factory methods) to also returntruewhen ALL of the following hold:isRecord()@JsonCreatorannotation (so an explicit annotation always wins)MapperFeatureis enabledWhen the heuristic fires, the Creator is treated as if it carried
@JsonCreator(mode = DELEGATING).New MapperFeature
MapperFeature.INFER_CREATOR_FROM_RECORD_ARGUMENT— default enabled.Javadoc will state:
Out of scope
@JsonPropertyannotations #5222.Test plan
New behavior:
static Foo of(Rec r)→ DELEGATINGRegression guards:
@JsonCreator(mode = PROPERTIES)on record-arg → PROPERTIES wins@JsonCreatorpresent in the class → record-arg ignoredBox<T>) → unchanged from currentMapperFeaturedisabled → byte-for-byte old behaviorExisting tests under
records/,DelegatingCreatorsTest,RecordExplicitCreatorsTest,DelegatingCreatorImplicitNamesTestmust continue to pass unchanged.Target
Jackson 3.x (branch
3.x) — behavioral change.Happy to take this on if the plan looks good.
Background discussion: #5222