Skip to content

Commit 044419c

Browse files
committed
Fixed #406
1 parent ce11121 commit 044419c

File tree

4 files changed

+82
-26
lines changed

4 files changed

+82
-26
lines changed

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Version: 2.3.2 (xx-xxx-2014)
88
(reported by Chris P, cpilsworth@github)
99
#398: Should deserialize empty (not null) URI from empty String
1010
(reported by pgieser@github)
11+
#406: @JsonTypeIdResolver not working with external type ids
12+
(repoted by Martin T)
1113
- Added `BeanSerializerBase._serializeObjectId()` needed by modules that
1214
override standard BeanSerializer; specifically, XML module.
1315

src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
* for converting between java types and type id included in JSON content.
1919
* In simplest cases this can be a simple class with static mapping between
2020
* type names and matching classes.
21+
*<p>
22+
* NOTE: since 2.4, applicable to properties as well (should have been long time
23+
* ago, but problem only found then)
2124
*/
22-
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
25+
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
2326
@Retention(RetentionPolicy.RUNTIME)
2427
@JacksonAnnotation
2528
public @interface JsonTypeIdResolver

src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
* used for handling serialization and deserialization of type information,
1010
* needed for handling of polymorphic types (or sometimes just for linking
1111
* abstract types to concrete types)
12+
*<p>
13+
* NOTE: since 2.4, applicable to properties as well (should have been long time
14+
* ago, but problem only found then)
1215
*/
13-
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
16+
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
1417
@Retention(RetentionPolicy.RUNTIME)
1518
@com.fasterxml.jackson.annotation.JacksonAnnotation
1619
public @interface JsonTypeResolver

src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java

+72-24
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,66 @@ public class TestCustomTypeIdResolver extends BaseMapTest
1717
{
1818
@JsonTypeInfo(use=Id.CUSTOM, include=As.WRAPPER_OBJECT)
1919
@JsonTypeIdResolver(CustomResolver.class)
20-
static class CustomBean {
20+
static abstract class CustomBean { }
21+
22+
static class CustomBeanImpl extends CustomBean {
2123
public int x;
2224

23-
public CustomBean() { }
24-
public CustomBean(int x) { this.x = x; }
25+
public CustomBeanImpl() { }
26+
public CustomBeanImpl(int x) { this.x = x; }
2527
}
26-
27-
static class CustomResolver implements TypeIdResolver
28-
{
28+
29+
static class ExtBeanWrapper {
30+
@JsonTypeInfo(use=Id.CUSTOM, include=As.EXTERNAL_PROPERTY, property="type")
31+
@JsonTypeIdResolver(ExtResolver.class)
32+
public ExtBean value;
33+
}
34+
35+
static class CustomResolver extends CustomResolverBase {
36+
// yes, static: just for test purposes, not real use
2937
static List<JavaType> initTypes;
3038

31-
public CustomResolver() { }
32-
33-
@Override
34-
public Id getMechanism() {
35-
return Id.CUSTOM;
39+
public CustomResolver() {
40+
super(CustomBean.class, CustomBeanImpl.class);
3641
}
3742

3843
@Override
39-
public String idFromValue(Object value)
40-
{
41-
if (value.getClass() == CustomBean.class) {
44+
public void init(JavaType baseType) {
45+
if (initTypes != null) {
46+
initTypes.add(baseType);
47+
}
48+
}
49+
}
50+
51+
static abstract class ExtBean { }
52+
53+
static class ExtBeanImpl extends ExtBean {
54+
public int y;
55+
56+
public ExtBeanImpl() { }
57+
public ExtBeanImpl(int y) { this.y = y; }
58+
}
59+
60+
static class ExtResolver extends CustomResolverBase {
61+
public ExtResolver() {
62+
super(ExtBean.class, ExtBeanImpl.class);
63+
}
64+
}
65+
66+
static class CustomResolverBase implements TypeIdResolver
67+
{
68+
protected final Class<?> superType;
69+
protected final Class<?> subType;
70+
71+
public CustomResolverBase(Class<?> baseType, Class<?> implType) {
72+
superType = baseType;
73+
subType = implType;
74+
}
75+
76+
@Override public Id getMechanism() { return Id.CUSTOM; }
77+
78+
@Override public String idFromValue(Object value) {
79+
if (superType.isAssignableFrom(value.getClass())) {
4280
return "*";
4381
}
4482
return "unknown";
@@ -50,17 +88,13 @@ public String idFromValueAndType(Object value, Class<?> type) {
5088
}
5189

5290
@Override
53-
public void init(JavaType baseType) {
54-
if (initTypes != null) {
55-
initTypes.add(baseType);
56-
}
57-
}
91+
public void init(JavaType baseType) { }
5892

5993
@Override
6094
public JavaType typeFromId(String id)
6195
{
6296
if ("*".equals(id)) {
63-
return TypeFactory.defaultInstance().constructType(CustomBean.class);
97+
return TypeFactory.defaultInstance().constructType(subType);
6498
}
6599
return null;
66100
}
@@ -77,24 +111,38 @@ public String idFromBaseType() {
77111
/**********************************************************
78112
*/
79113

114+
private final ObjectMapper MAPPER = objectMapper();
115+
80116
// for [JACKSON-359]
81117
public void testCustomTypeIdResolver() throws Exception
82118
{
83-
ObjectMapper m = new ObjectMapper();
84119
List<JavaType> types = new ArrayList<JavaType>();
85120
CustomResolver.initTypes = types;
86-
String json = m.writeValueAsString(new CustomBean[] { new CustomBean(28) });
121+
String json = MAPPER.writeValueAsString(new CustomBean[] { new CustomBeanImpl(28) });
87122
assertEquals("[{\"*\":{\"x\":28}}]", json);
88123
assertEquals(1, types.size());
89124
assertEquals(CustomBean.class, types.get(0).getRawClass());
90125

91126
types = new ArrayList<JavaType>();
92127
CustomResolver.initTypes = types;
93-
CustomBean[] result = m.readValue(json, CustomBean[].class);
128+
CustomBean[] result = MAPPER.readValue(json, CustomBean[].class);
94129
assertNotNull(result);
95130
assertEquals(1, result.length);
96-
assertEquals(28, result[0].x);
131+
assertEquals(28, ((CustomBeanImpl) result[0]).x);
97132
assertEquals(1, types.size());
98133
assertEquals(CustomBean.class, types.get(0).getRawClass());
99134
}
135+
136+
public void testCustomWithExternal() throws Exception
137+
{
138+
ExtBeanWrapper w = new ExtBeanWrapper();
139+
w.value = new ExtBeanImpl(12);
140+
141+
String json = MAPPER.writeValueAsString(w);
142+
143+
ExtBeanWrapper out = MAPPER.readValue(json, ExtBeanWrapper.class);
144+
assertNotNull(out);
145+
146+
assertEquals(12, ((ExtBeanImpl) out.value).y);
147+
}
100148
}

0 commit comments

Comments
 (0)