Skip to content

Commit e5be4cc

Browse files
committed
Fix #2686: @JsonBackReference not working with Builder
1 parent fa318d2 commit e5be4cc

File tree

2 files changed

+57
-8
lines changed

2 files changed

+57
-8
lines changed

src/main/java/tools/jackson/databind/deser/BeanDeserializerFactory.java

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,9 @@ public ValueDeserializer<Object> buildBeanDeserializer(DeserializationContext ct
274274
addObjectIdReader(ctxt, beanDescRef, builder);
275275

276276
// managed/back reference fields/setters need special handling... first part
277-
addBackReferenceProperties(ctxt, beanDescRef, builder);
277+
// [databind#2686]: targetType For Builder pattern, this method is not using.
278+
// So this value is null.
279+
addBackReferenceProperties(ctxt, beanDescRef, builder, null);
278280
addInjectables(ctxt, beanDescRef, builder);
279281

280282
final DeserializationConfig config = ctxt.getConfig();
@@ -334,7 +336,8 @@ protected ValueDeserializer<Object> buildBuilderBasedDeserializer(
334336
addObjectIdReader(ctxt, builderDescRef, builder);
335337

336338
// managed/back reference fields/setters need special handling... first part
337-
addBackReferenceProperties(ctxt, builderDescRef, builder);
339+
// [databind#2686]: For Builder pattern, pass target type so that back-reference
340+
addBackReferenceProperties(ctxt, builderDescRef, builder, valueType);
338341
addInjectables(ctxt, builderDescRef, builder);
339342

340343
JsonPOJOBuilder.Value builderConfig = ctxt.getAnnotationIntrospector()
@@ -736,7 +739,8 @@ && isIgnorableType(ctxt, property, rawPropertyType, ignoredTypes)) {
736739
* and if so add them to bean, to be linked during resolution phase.
737740
*/
738741
protected void addBackReferenceProperties(DeserializationContext ctxt,
739-
BeanDescription.Supplier beanDescRef, BeanDeserializerBuilder builder)
742+
BeanDescription.Supplier beanDescRef, BeanDeserializerBuilder builder,
743+
JavaType targetType)
740744
{
741745
// and then back references, not necessarily found as regular properties
742746
List<BeanPropertyDefinition> refProps = beanDescRef.get().findBackReferences();
@@ -764,8 +768,31 @@ protected void addBackReferenceProperties(DeserializationContext ctxt,
764768
refProp.getName());
765769
}
766770
String refName = refProp.findReferenceName();
767-
builder.addBackReferenceProperty(refName, constructSettableProperty(ctxt,
768-
beanDescRef, refProp, refProp.getPrimaryType()));
771+
SettableBeanProperty backRefProp;
772+
773+
if (targetType != null) {
774+
// [databind#2686]: Handle Builder
775+
backRefProp = constructBuilderBackRefProperty(ctxt,
776+
targetType, refProp);
777+
} else {
778+
// normal
779+
backRefProp = constructSettableProperty(ctxt,
780+
beanDescRef, refProp, refProp.getPrimaryType());
781+
}
782+
783+
if (backRefProp != null) {
784+
builder.addBackReferenceProperty(refName, backRefProp);
785+
} else {
786+
if (targetType != null) {
787+
ctxt.reportBadTypeDefinition(beanDescRef,
788+
"Cannot find back-reference field '%s' in target type '%s' for Builder-based deserialization",
789+
refProp.getName(), targetType.getRawClass().getName());
790+
} else {
791+
ctxt.reportBadTypeDefinition(beanDescRef,
792+
"Cannot resolve back-reference property '%s'",
793+
refProp.getName());
794+
}
795+
}
769796
}
770797
}
771798
}
@@ -1068,4 +1095,28 @@ protected void _validateSubType(DeserializationContext ctxt, JavaType type,
10681095
{
10691096
SubTypeValidator.instance().validateSubType(ctxt, type, beanDescRef);
10701097
}
1098+
1099+
/**
1100+
* Helper method for constructing back-reference property when using Builder pattern.
1101+
*
1102+
* @since 3.1
1103+
*/
1104+
protected SettableBeanProperty constructBuilderBackRefProperty(DeserializationContext ctxt,
1105+
JavaType targetType, BeanPropertyDefinition builderRefProp)
1106+
{
1107+
BeanDescription.Supplier targetDescRef = ctxt.lazyIntrospectBeanDescription(targetType);
1108+
BeanDescription targetDesc = targetDescRef.get();
1109+
1110+
// find back reference with same field
1111+
String propName = builderRefProp.getName();
1112+
for (BeanPropertyDefinition propDef : targetDesc.findProperties()) {
1113+
if (propName.equals(propDef.getName()) && propDef.hasField()) {
1114+
AnnotatedField field = propDef.getField();
1115+
JavaType propertyType = field.getType();
1116+
return constructSettableProperty(ctxt, targetDescRef, propDef, propertyType);
1117+
}
1118+
}
1119+
1120+
return null;
1121+
}
10711122
}

src/test/java/tools/jackson/databind/tofix/BuilderWithBackRef2686Test.java renamed to src/test/java/tools/jackson/databind/struct/BuilderWithBackRef2686Test.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package tools.jackson.databind.tofix;
1+
package tools.jackson.databind.struct;
22

33
import java.beans.ConstructorProperties;
44

@@ -11,7 +11,6 @@
1111
import tools.jackson.databind.annotation.JsonDeserialize;
1212
import tools.jackson.databind.annotation.JsonPOJOBuilder;
1313
import tools.jackson.databind.testutil.DatabindTestUtil;
14-
import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected;
1514

1615
import static org.junit.jupiter.api.Assertions.assertNotNull;
1716

@@ -91,7 +90,6 @@ Content build() {
9190

9291
private final ObjectMapper MAPPER = newJsonMapper();
9392

94-
@JacksonTestFailureExpected
9593
@Test
9694
void buildWithBackRefs2686() throws Exception {
9795
Container container = new Container();

0 commit comments

Comments
 (0)