Skip to content

Commit 6124d9b

Browse files
authored
Fix ion null deserialization (#296)
1 parent b748b28 commit 6124d9b

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

ion/src/main/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializer.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.databind.DeserializationContext;
2121
import com.fasterxml.jackson.databind.JsonMappingException;
2222
import com.fasterxml.jackson.databind.JsonDeserializer;
23+
import com.fasterxml.jackson.databind.util.AccessPattern;
2324
import com.fasterxml.jackson.dataformat.ion.IonParser;
2425
import com.amazon.ion.IonSystem;
2526
import com.amazon.ion.IonValue;
@@ -53,4 +54,26 @@ public IonValue deserialize(JsonParser jp, DeserializationContext ctxt) throws I
5354
throw JsonMappingException.from(jp, "Cannot deserialize embedded object type "
5455
+ embeddedObject.getClass().getCanonicalName() + " into IonValue");
5556
}
57+
58+
@Override
59+
public IonValue getNullValue(DeserializationContext ctxt) throws JsonMappingException {
60+
try {
61+
Object embeddedObj = ctxt.getParser().getEmbeddedObject();
62+
if (embeddedObj instanceof IonValue) {
63+
IonValue iv = (IonValue) embeddedObj;
64+
if (iv.isNullValue()) {
65+
return iv;
66+
}
67+
}
68+
69+
return super.getNullValue(ctxt);
70+
} catch (IOException e) {
71+
throw JsonMappingException.from(ctxt, e.toString());
72+
}
73+
}
74+
75+
@Override
76+
public AccessPattern getNullAccessPattern() {
77+
return AccessPattern.DYNAMIC;
78+
}
5679
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package com.fasterxml.jackson.dataformat.ion.ionvalue;
2+
3+
import com.amazon.ion.IonSystem;
4+
import com.amazon.ion.IonValue;
5+
import com.amazon.ion.system.IonSystemBuilder;
6+
import com.fasterxml.jackson.annotation.JsonAnyGetter;
7+
import com.fasterxml.jackson.annotation.JsonAnySetter;
8+
import com.fasterxml.jackson.databind.util.AccessPattern;
9+
import org.junit.Test;
10+
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
14+
import static org.junit.Assert.assertEquals;
15+
import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE;
16+
17+
public class IonValueDeserializerTest {
18+
private static class Data<T> {
19+
20+
private final Map<String, T> map = new HashMap<>();
21+
22+
@JsonAnySetter
23+
public void put(String key, T value) {
24+
map.put(key, value);
25+
}
26+
27+
@JsonAnyGetter
28+
public Map<String, T> getAllData() {
29+
return map;
30+
}
31+
32+
@Override
33+
public int hashCode() {
34+
return map.hashCode();
35+
}
36+
37+
@Override
38+
public String toString() {
39+
return map.toString();
40+
}
41+
42+
@Override
43+
public boolean equals(Object obj) {
44+
if (this == obj) {
45+
return true;
46+
}
47+
if (obj == null) {
48+
return false;
49+
}
50+
if (getClass() != obj.getClass()) {
51+
return false;
52+
}
53+
Data other = (Data) obj;
54+
return map.equals(other.map);
55+
}
56+
57+
}
58+
59+
private static class StringData extends Data<String> {
60+
61+
}
62+
63+
private static class IonValueData extends Data<IonValue> {
64+
65+
}
66+
67+
private static final IonSystem SYSTEM = IonSystemBuilder.standard().build();
68+
private static final IonValueMapper ION_VALUE_MAPPER = new IonValueMapper(SYSTEM, SNAKE_CASE);
69+
70+
@Test
71+
public void shouldBeAbleToDeserialize() throws Exception {
72+
IonValue ion = ion("{a:1, b:2, c:3}");
73+
74+
IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class);
75+
76+
assertEquals(3, data.getAllData().size());
77+
assertEquals(ion("1"), data.getAllData().get("a"));
78+
assertEquals(ion("2"), data.getAllData().get("b"));
79+
assertEquals(ion("3"), data.getAllData().get("c"));
80+
}
81+
82+
@Test
83+
public void shouldBeAbleToDeserializeIncludingNullList() throws Exception {
84+
IonValue ion = ion("{a:1, b:2, c:null.list}");
85+
86+
IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class);
87+
88+
assertEquals(3, data.getAllData().size());
89+
assertEquals(ion("1"), data.getAllData().get("a"));
90+
assertEquals(ion("2"), data.getAllData().get("b"));
91+
assertEquals(ion("null.list"), data.getAllData().get("c"));
92+
}
93+
94+
@Test
95+
public void shouldBeAbleToDeserializeNullList() throws Exception {
96+
IonValue ion = ion("{c:null.list}");
97+
98+
IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class);
99+
100+
assertEquals(1, data.getAllData().size());
101+
assertEquals(SYSTEM.newNullList(), data.getAllData().get("c"));
102+
}
103+
104+
@Test
105+
public void shouldBeAbleToDeserializeNullStruct() throws Exception {
106+
IonValue ion = ion("{c:null.struct}");
107+
108+
IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class);
109+
110+
assertEquals(1, data.getAllData().size());
111+
assertEquals(SYSTEM.newNullStruct(), data.getAllData().get("c"));
112+
}
113+
114+
@Test
115+
public void shouldBeAbleToDeserializeNullValue() throws Exception {
116+
IonValue ion = SYSTEM.newNull();
117+
118+
IonValue data = ION_VALUE_MAPPER.parse(ion, IonValue.class);
119+
120+
assertEquals(ion, data);
121+
}
122+
123+
@Test
124+
public void shouldBeAbleToDeserializeAnnotatedNullStruct() throws Exception {
125+
IonValue ion = ion("foo::null.struct");
126+
127+
IonValue data = ION_VALUE_MAPPER.parse(ion, IonValue.class);
128+
129+
assertEquals(ion, data);
130+
assertEquals(1, data.getTypeAnnotations().length);
131+
assertEquals("foo", data.getTypeAnnotations()[0]);
132+
}
133+
134+
@Test
135+
public void shouldBeAbleToDeserializeAnnotatedNullList() throws Exception {
136+
IonValue ion = ion("foo::null.list");
137+
138+
IonValue data = ION_VALUE_MAPPER.parse(ion, IonValue.class);
139+
140+
assertEquals(ion, data);
141+
assertEquals(1, data.getTypeAnnotations().length);
142+
assertEquals("foo", data.getTypeAnnotations()[0]);
143+
}
144+
145+
@Test
146+
public void shouldBeAbleToSerializeAndDeserializePojo() throws Exception {
147+
IonValueData source = new IonValueData();
148+
source.put("a", ion("1"));
149+
source.put("c", ion("null.list"));
150+
151+
IonValue data = ION_VALUE_MAPPER.serialize(source);
152+
IonValueData result = ION_VALUE_MAPPER.parse(data, IonValueData.class);
153+
154+
assertEquals(source, result);
155+
}
156+
157+
@Test
158+
public void shouldBeAbleToSerializeAndDeserializeStringData() throws Exception {
159+
StringData source = new StringData();
160+
source.put("a", "1");
161+
source.put("b", null);
162+
163+
IonValue data = ION_VALUE_MAPPER.serialize(source);
164+
StringData result = ION_VALUE_MAPPER.parse(data, StringData.class);
165+
166+
assertEquals(source, result);
167+
}
168+
169+
@Test
170+
public void shouldOverrideNullAccessPatternToBeDynamic() {
171+
IonValueDeserializer deserializer = new IonValueDeserializer();
172+
assertEquals(AccessPattern.DYNAMIC, deserializer.getNullAccessPattern());
173+
}
174+
175+
private static IonValue ion(String value) {
176+
return SYSTEM.singleValue(value);
177+
}
178+
}

0 commit comments

Comments
 (0)