Skip to content

Commit e19083c

Browse files
author
Ville Koskela
committed
Extend support for deserializing via builder if the builder has type bindings by inferring those bindings from the value type. This behavior is not ideal, but addresses the common case, and is controlled by a MapperFeature.
1 parent 75e9280 commit e19083c

File tree

4 files changed

+54
-13
lines changed

4 files changed

+54
-13
lines changed

src/main/java/com/fasterxml/jackson/databind/MapperFeature.java

+12
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,18 @@ public enum MapperFeature implements ConfigFeature
198198
*/
199199
USE_STATIC_TYPING(false),
200200

201+
/**
202+
* Feature that enables inferring builder type bindings from the value type
203+
* being deserialized. This requires that the generic type declaration on
204+
* the value type match that on the builder exactly.
205+
*<p>
206+
* Feature is disabled by default which means that deserialization does
207+
* not support deserializing types via builders with type parameters.
208+
*<p>
209+
* See: https://github.com/FasterXML/jackson-databind/issues/921
210+
*/
211+
INFER_BUILDER_TYPE_BINDINGS(false),
212+
201213
/*
202214
/******************************************************
203215
/* View-related features

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,18 @@ public JsonDeserializer<Object> createBeanDeserializer(DeserializationContext ct
178178
}
179179

180180
@Override
181-
public JsonDeserializer<Object> createBuilderBasedDeserializer(DeserializationContext ctxt,
182-
JavaType valueType, BeanDescription beanDesc, Class<?> builderClass)
183-
throws JsonMappingException
181+
public JsonDeserializer<Object> createBuilderBasedDeserializer(
182+
DeserializationContext ctxt, JavaType valueType, BeanDescription beanDesc,
183+
Class<?> builderClass)
184+
throws JsonMappingException
184185
{
185186
// First: need a BeanDescription for builder class
186-
JavaType builderType = ctxt.constructType(builderClass);
187+
JavaType builderType;
188+
if (ctxt.isEnabled(MapperFeature.INFER_BUILDER_TYPE_BINDINGS)) {
189+
builderType = ctxt.getTypeFactory().constructParametricType(builderClass, valueType.getBindings());
190+
} else {
191+
builderType = ctxt.constructType(builderClass);
192+
}
187193
BeanDescription builderDesc = ctxt.getConfig().introspectForBuilder(builderType);
188194
return buildBuilderBasedDeserializer(ctxt, valueType, builderDesc);
189195
}

src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,15 @@ public JavaType constructParametricType(Class<?> parametrized, Class<?>... param
902902
*/
903903
public JavaType constructParametricType(Class<?> rawType, JavaType... parameterTypes)
904904
{
905-
return _fromClass(null, rawType, TypeBindings.create(rawType, parameterTypes));
905+
return constructParametricType(rawType, TypeBindings.create(rawType, parameterTypes));
906+
}
907+
908+
/**
909+
* @since 3.0
910+
*/
911+
public JavaType constructParametricType(Class<?> rawType, TypeBindings parameterTypes)
912+
{
913+
return _fromClass(null, rawType, parameterTypes);
906914
}
907915

908916
/*

src/test/java/com/fasterxml/jackson/failing/BuilderDeserializationTest921.java renamed to src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithTypeParametersTest.java

+23-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
package com.fasterxml.jackson.failing;
1+
package com.fasterxml.jackson.databind.deser.builder;
22

3-
import java.util.List;
4-
5-
import com.fasterxml.jackson.annotation.*;
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
65
import com.fasterxml.jackson.core.type.TypeReference;
7-
import com.fasterxml.jackson.databind.*;
6+
import com.fasterxml.jackson.databind.BaseMapTest;
7+
import com.fasterxml.jackson.databind.MapperFeature;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
89
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
10+
import java.util.LinkedHashMap;
11+
import java.util.List;
912

10-
public class BuilderDeserializationTest921
13+
public class BuilderWithTypeParametersTest
1114
extends BaseMapTest
1215
{
1316
public static class MyPOJO {
@@ -77,15 +80,27 @@ public MyGenericPOJOWithCreator<T> build() {
7780
}
7881
}
7982

80-
public void testWithBuilder() throws Exception {
83+
public void testWithBuilderInferringBindings() throws Exception {
84+
final ObjectMapper mapper = new ObjectMapper();
85+
mapper.enable(MapperFeature.INFER_BUILDER_TYPE_BINDINGS);
86+
final String json = aposToQuotes("{ 'data': [ { 'x': 'x', 'y': 'y' } ] }");
87+
final MyGenericPOJO<MyPOJO> deserialized =
88+
mapper.readValue(json, new TypeReference<MyGenericPOJO<MyPOJO>>() {});
89+
assertEquals(1, deserialized.data.size());
90+
Object ob = deserialized.data.get(0);
91+
assertNotNull(ob);
92+
assertEquals(MyPOJO.class, ob.getClass());
93+
}
94+
95+
public void testWithBuilderWithoutInferringBindings() throws Exception {
8196
final ObjectMapper mapper = new ObjectMapper();
8297
final String json = aposToQuotes("{ 'data': [ { 'x': 'x', 'y': 'y' } ] }");
8398
final MyGenericPOJO<MyPOJO> deserialized =
8499
mapper.readValue(json, new TypeReference<MyGenericPOJO<MyPOJO>>() {});
85100
assertEquals(1, deserialized.data.size());
86101
Object ob = deserialized.data.get(0);
87102
assertNotNull(ob);
88-
assertEquals(MyPOJO.class, ob.getClass());
103+
assertEquals(LinkedHashMap.class, ob.getClass());
89104
}
90105

91106
public void testWithCreator() throws Exception {

0 commit comments

Comments
 (0)