Skip to content

Commit c50dadd

Browse files
Binh Trancowtowncoder
Binh Tran
authored andcommitted
Allow IonObjectMapper with class name annotation introspector to deserialize non-annotated payloads.
Currently, an Ion Mapper with Classname Id resolver (and likely other kind of type id resolver) loses the ability to deserialize plain, non-annotated payload using only generic hints provided through type reference. This bug was likely because of this commit [1]. The fix here is to construct the type with full generic information instead of just using defaultImpl (which is now just a class). [1] FasterXML/jackson-databind@596c6dd#diff-48b058c7384ce3f9fc9e7440fc7dc804L89
1 parent 208b0a7 commit c50dadd

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

ion/src/main/java/com/fasterxml/jackson/dataformat/ion/polymorphism/IonAnnotationTypeResolverBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType b
7676
@Override
7777
public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) {
7878
JavaType defImplType = (defaultImpl == null) ? null
79-
: config.constructType(defaultImpl);
79+
: config.constructSpecializedType(baseType, defaultImpl);
8080
return new IonAnnotationTypeDeserializer(baseType,
8181
typeIdResolver, null, typeIdVisible, defImplType);
8282
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.fasterxml.jackson.dataformat.ion.polymorphism;
2+
3+
import com.amazon.ion.IonValue;
4+
import com.fasterxml.jackson.core.Version;
5+
import com.fasterxml.jackson.core.type.TypeReference;
6+
import com.fasterxml.jackson.databind.JavaType;
7+
import com.fasterxml.jackson.databind.cfg.MapperConfig;
8+
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
9+
import com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver;
10+
import com.fasterxml.jackson.databind.module.SimpleModule;
11+
import com.fasterxml.jackson.dataformat.ion.IonObjectMapper;
12+
import org.junit.Before;
13+
import org.junit.BeforeClass;
14+
import org.junit.Test;
15+
16+
import java.io.IOException;
17+
18+
import static org.junit.Assert.assertEquals;
19+
20+
/**
21+
* This test checks that {@link IonAnnotationTypeDeserializer} with {@link IonAnnotationIntrospector} expecting class
22+
* name can still deserialize regular Json/Ion (specifically ION without class name) payloads.
23+
*
24+
* @author Binh Tran
25+
*/
26+
public class IonAnnotationTypeDeserializerWithClassNameAnnotationTest {
27+
28+
private static IonValue ionValueWithoutAnnotation;
29+
private static IonValue ionValueWithAnnotation;
30+
private IonObjectMapper mapperUnderTest;
31+
32+
@BeforeClass
33+
public static void setupClass() throws IOException {
34+
ClassA inner = new ClassA();
35+
inner.value = 42;
36+
37+
ClassB<ClassA> outer = new ClassB<>();
38+
outer.content = inner;
39+
40+
IonObjectMapper mapper = new IonObjectMapper();
41+
ionValueWithoutAnnotation = mapper.writeValueAsIonValue(outer);
42+
43+
mapper = constructIomWithClassNameIdResolver();
44+
ionValueWithAnnotation = mapper.writeValueAsIonValue(outer);
45+
}
46+
47+
@Before
48+
public void setup() {
49+
// Important: since Jackson caches type resolving information, we need to create a separate mapper for testing.
50+
mapperUnderTest = constructIomWithClassNameIdResolver();
51+
}
52+
53+
@Test
54+
public void testDeserializeAnnotatedPayload() throws IOException {
55+
IonObjectMapper mapper = constructIomWithClassNameIdResolver();
56+
57+
ClassB<ClassA> newObj = mapper.readValue(ionValueWithAnnotation, new TypeReference<ClassB<ClassA>>() {});
58+
59+
ClassA content = newObj.content;
60+
assertEquals(42, content.value);
61+
}
62+
63+
@Test
64+
public void testDeserializeNonAnnotatedPayload() throws IOException {
65+
IonObjectMapper mapper = constructIomWithClassNameIdResolver();
66+
67+
ClassB<ClassA> newObj = mapper.readValue(ionValueWithoutAnnotation, new TypeReference<ClassB<ClassA>>() {});
68+
69+
ClassA content = newObj.content;
70+
assertEquals(42, content.value);
71+
}
72+
73+
private static IonObjectMapper constructIomWithClassNameIdResolver() {
74+
IonObjectMapper mapper = new IonObjectMapper();
75+
mapper.registerModule(new IonAnnotationModule());
76+
77+
return mapper;
78+
}
79+
80+
// Helper classes to reproduce the issue.
81+
82+
static class IonAnnotationModule extends SimpleModule {
83+
private static final long serialVersionUID = 3018097049612590165L;
84+
85+
IonAnnotationModule() {
86+
super("IonAnnotationMod", Version.unknownVersion());
87+
}
88+
89+
@Override
90+
public void setupModule(SetupContext context) {
91+
IonAnnotationIntrospector introspector = new ClassNameIonAnnotationIntrospector();
92+
context.appendAnnotationIntrospector(introspector);
93+
}
94+
}
95+
96+
static class ClassNameIonAnnotationIntrospector extends IonAnnotationIntrospector {
97+
private static final long serialVersionUID = -5519199636013243472L;
98+
99+
ClassNameIonAnnotationIntrospector() {
100+
super(true);
101+
}
102+
103+
@Override
104+
protected TypeIdResolver defaultIdResolver(MapperConfig<?> config, JavaType baseType) {
105+
return new ClassNameIdResolver(baseType, config.getTypeFactory(), config.getPolymorphicTypeValidator());
106+
}
107+
}
108+
109+
private static class ClassA {
110+
public int value;
111+
}
112+
113+
private static class ClassB<T> {
114+
public T content;
115+
}
116+
}

0 commit comments

Comments
 (0)