Skip to content

Commit 18520e9

Browse files
authored
Merge branch '3.x' into 5405-map-format-shape-bug
2 parents 58e8c48 + 7ccfc9e commit 18520e9

File tree

5 files changed

+59
-24
lines changed

5 files changed

+59
-24
lines changed

release-notes/CREDITS

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,15 @@ Oliver Drotbohm (@odrotbohm)
127127
* Contributed fix for #4629: `@JsonIncludeProperties` and `@JsonIgnoreProperties`
128128
ignored when deserializing Records
129129
[3.1.0]
130-
130+
* Contributed fix for #5115: `@JsonUnwrapped` Record deserialization can't handle
131+
name collision
132+
[3.1.0]
133+
134+
Viktor Szathmáry (@phraktle)
135+
* Reported #5115: `@JsonUnwrapped` Record deserialization can't handle name collision
136+
(reported by Viktor S)
137+
[3.1.0]
138+
131139
Hélios Gilles (@RoiSoleil)
132140
* Contributed #5413: Add/support forward reference resolution for array values
133141
[3.1.0]

release-notes/VERSION

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ Versions: 3.x (for earlier see VERSION-2.x)
2424
creator property" when deserializing JSON with dup property to single-property Record
2525
(reported by @sseelmann)
2626
(fix contributed by @JacksonJang)
27+
#5115: `@JsonUnwrapped` Record deserialization can't handle name collision
28+
(reported by Viktor S)
29+
(fix contributed by @JacksonJang)
30+
#5184: `@JsonIgnore` on record method applied to record matching field
31+
at deserialization
32+
(reported by @emouty)
2733
#5350: Add `DeserializationFeature.USE_NULL_FOR_MISSING_REFERENCE_VALUES` for
2834
selecting `null` vs "empty/absent" value when deserializing missing `Optional` value
2935
#5361: Fix Maven SBOM publishing (worked in 3.0.0-rc4 but not in rc5 or later)

src/main/java/tools/jackson/databind/introspect/POJOPropertiesCollector.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,33 +1054,38 @@ private void _addCreatorParams(Map<String, POJOPropertyBuilder> props,
10541054
final PropertyName explName = ctor.explicitName(i);
10551055
PropertyName implName = ctor.implicitName(i);
10561056
final boolean hasExplicit = (explName != null);
1057-
final POJOPropertyBuilder prop;
1058-
1059-
// neither implicit nor explicit name?
1060-
if (!hasExplicit && (implName == null)) {
1061-
boolean isUnwrapping = _annotationIntrospector.findUnwrappingNameTransformer(_config, param) != null;
1057+
final boolean hasImplicit = (implName != null);
10621058

1063-
if (isUnwrapping) {
1059+
// First: check "Unwrapped" unless explicit name
1060+
if (!hasExplicit) {
1061+
var unwrapper = _annotationIntrospector.findUnwrappingNameTransformer(_config, param);
1062+
if (unwrapper != null) {
10641063
// If unwrapping, can use regardless of name; we will use a placeholder name
10651064
// anyway to try to avoid name conflicts.
10661065
PropertyName name = UnwrappedPropertyHandler.creatorParamName(param.getIndex());
1067-
prop = _property(props, name);
1066+
final POJOPropertyBuilder prop = _property(props, name);
10681067
prop.addCtor(param, name, false, true, false);
1069-
} else {
1068+
creatorProps.add(prop);
1069+
continue;
1070+
}
1071+
if (!hasImplicit) {
10701072
// Without name, cannot make use of this creator parameter -- may or may not
10711073
// be a problem, verified at a later point.
1072-
prop = null;
1074+
creatorProps.add(null);
1075+
continue;
10731076
}
1077+
}
1078+
1079+
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
1080+
final POJOPropertyBuilder prop;
1081+
if (hasImplicit) {
1082+
String n = _checkRenameByField(implName.getSimpleName());
1083+
implName = PropertyName.construct(n);
1084+
prop = _property(props, implName);
10741085
} else {
1075-
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
1076-
if (implName != null) {
1077-
String n = _checkRenameByField(implName.getSimpleName());
1078-
implName = PropertyName.construct(n);
1079-
}
1080-
prop = (implName == null)
1081-
? _property(props, explName) : _property(props, implName);
1082-
prop.addCtor(param, hasExplicit ? explName : implName, hasExplicit, true, false);
1086+
prop = _property(props, explName);
10831087
}
1088+
prop.addCtor(param, hasExplicit ? explName : implName, hasExplicit, true, false);
10841089
creatorProps.add(prop);
10851090
}
10861091
ctor.assignPropertyDefs(creatorProps);
@@ -1201,6 +1206,24 @@ else if (Boolean.TRUE.equals(_annotationIntrospector.hasAsValue(_config, m))) {
12011206
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
12021207
implName = _checkRenameByField(implName);
12031208
boolean ignore = _annotationIntrospector.hasIgnoreMarker(_config, m);
1209+
// 03-Dec-2025, tatu: [databind#5184]: Not the cleanest fix but here goes...
1210+
// (why not clean? Ideally accessor reconciliation solved the issue, not
1211+
// special case rule like done here)
1212+
// For Records, prevent "get"-prefix methods with @JsonIgnore from incorrectly
1213+
// affecting Record component fields (and thereby Creator parameters).
1214+
// For example, if getter method is "getValue()" with @JsonIgnore and there's a
1215+
// record component "value", the method should not cause the field to be ignored since
1216+
// the actual accessor is "value()".
1217+
// We check: is this a Record, does the method name NOT match the derived property name
1218+
// (indicating prefix was stripped), does the property already exist (from a record field),
1219+
// and does this method have @JsonIgnore?
1220+
if (_isRecordType && !nameExplicit && ignore && !implName.equals(m.getName())) {
1221+
POJOPropertyBuilder prop = props.get(implName);
1222+
if (prop != null && prop.hasField()) {
1223+
// Skip adding this getter to avoid its @JsonIgnore affecting the record field
1224+
return;
1225+
}
1226+
}
12041227
_property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore);
12051228
}
12061229

src/test/java/tools/jackson/databind/records/tofix/RecordUnwrapped5115Test.java renamed to src/test/java/tools/jackson/databind/records/RecordUnwrapped5115Test.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
package tools.jackson.databind.records.tofix;
1+
package tools.jackson.databind.records;
22

33
import org.junit.jupiter.api.Test;
44

55
import com.fasterxml.jackson.annotation.JsonUnwrapped;
6+
67
import tools.jackson.databind.ObjectMapper;
78
import tools.jackson.databind.testutil.DatabindTestUtil;
8-
import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected;
99

1010
import static org.junit.jupiter.api.Assertions.assertEquals;
1111

@@ -62,7 +62,6 @@ void unwrappedRecordShouldRoundTripPass() throws Exception
6262
assertEquals(input, output);
6363
}
6464

65-
@JacksonTestFailureExpected
6665
@Test
6766
void unwrappedRecordShouldRoundTrip() throws Exception
6867
{
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package tools.jackson.databind.records.tofix;
1+
package tools.jackson.databind.records;
22

33
import java.util.Optional;
44

@@ -8,7 +8,6 @@
88
import com.fasterxml.jackson.annotation.JsonProperty;
99
import tools.jackson.databind.ObjectMapper;
1010
import tools.jackson.databind.testutil.DatabindTestUtil;
11-
import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected;
1211

1312
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
1413

@@ -44,7 +43,7 @@ public Optional<String> getValue() {
4443

4544
private static final ObjectMapper MAPPER = newJsonMapper();
4645

47-
@JacksonTestFailureExpected
46+
// [databind#5184]
4847
@Test
4948
void should_deserialize_json_to_test_data() throws Exception {
5049
String json = """

0 commit comments

Comments
 (0)