diff --git a/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializer.java b/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializer.java index ed435ed7a..128f503ca 100644 --- a/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializer.java +++ b/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializer.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.util.AccessPattern; import com.fasterxml.jackson.dataformat.ion.IonParser; import com.amazon.ion.IonSystem; import com.amazon.ion.IonValue; @@ -53,4 +54,26 @@ public IonValue deserialize(JsonParser jp, DeserializationContext ctxt) throws I throw JsonMappingException.from(jp, "Cannot deserialize embedded object type " + embeddedObject.getClass().getCanonicalName() + " into IonValue"); } + + @Override + public IonValue getNullValue(DeserializationContext ctxt) throws JsonMappingException { + try { + Object embeddedObj = ctxt.getParser().getEmbeddedObject(); + if (embeddedObj instanceof IonValue) { + IonValue iv = (IonValue) embeddedObj; + if (iv.isNullValue()) { + return iv; + } + } + + return super.getNullValue(ctxt); + } catch (IOException e) { + throw JsonMappingException.from(ctxt, e.toString()); + } + } + + @Override + public AccessPattern getNullAccessPattern() { + return AccessPattern.DYNAMIC; + } } diff --git a/ion/src/test/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializerTest.java b/ion/src/test/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializerTest.java new file mode 100644 index 000000000..88b0232bb --- /dev/null +++ b/ion/src/test/java/com/fasterxml/jackson/dataformat/ion/ionvalue/IonValueDeserializerTest.java @@ -0,0 +1,178 @@ +package com.fasterxml.jackson.dataformat.ion.ionvalue; + +import com.amazon.ion.IonSystem; +import com.amazon.ion.IonValue; +import com.amazon.ion.system.IonSystemBuilder; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.databind.util.AccessPattern; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE; + +public class IonValueDeserializerTest { + private static class Data { + + private final Map map = new HashMap<>(); + + @JsonAnySetter + public void put(String key, T value) { + map.put(key, value); + } + + @JsonAnyGetter + public Map getAllData() { + return map; + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public String toString() { + return map.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Data other = (Data) obj; + return map.equals(other.map); + } + + } + + private static class StringData extends Data { + + } + + private static class IonValueData extends Data { + + } + + private static final IonSystem SYSTEM = IonSystemBuilder.standard().build(); + private static final IonValueMapper ION_VALUE_MAPPER = new IonValueMapper(SYSTEM, SNAKE_CASE); + + @Test + public void shouldBeAbleToDeserialize() throws Exception { + IonValue ion = ion("{a:1, b:2, c:3}"); + + IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class); + + assertEquals(3, data.getAllData().size()); + assertEquals(ion("1"), data.getAllData().get("a")); + assertEquals(ion("2"), data.getAllData().get("b")); + assertEquals(ion("3"), data.getAllData().get("c")); + } + + @Test + public void shouldBeAbleToDeserializeIncludingNullList() throws Exception { + IonValue ion = ion("{a:1, b:2, c:null.list}"); + + IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class); + + assertEquals(3, data.getAllData().size()); + assertEquals(ion("1"), data.getAllData().get("a")); + assertEquals(ion("2"), data.getAllData().get("b")); + assertEquals(ion("null.list"), data.getAllData().get("c")); + } + + @Test + public void shouldBeAbleToDeserializeNullList() throws Exception { + IonValue ion = ion("{c:null.list}"); + + IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class); + + assertEquals(1, data.getAllData().size()); + assertEquals(SYSTEM.newNullList(), data.getAllData().get("c")); + } + + @Test + public void shouldBeAbleToDeserializeNullStruct() throws Exception { + IonValue ion = ion("{c:null.struct}"); + + IonValueData data = ION_VALUE_MAPPER.parse(ion, IonValueData.class); + + assertEquals(1, data.getAllData().size()); + assertEquals(SYSTEM.newNullStruct(), data.getAllData().get("c")); + } + + @Test + public void shouldBeAbleToDeserializeNullValue() throws Exception { + IonValue ion = SYSTEM.newNull(); + + IonValue data = ION_VALUE_MAPPER.parse(ion, IonValue.class); + + assertEquals(ion, data); + } + + @Test + public void shouldBeAbleToDeserializeAnnotatedNullStruct() throws Exception { + IonValue ion = ion("foo::null.struct"); + + IonValue data = ION_VALUE_MAPPER.parse(ion, IonValue.class); + + assertEquals(ion, data); + assertEquals(1, data.getTypeAnnotations().length); + assertEquals("foo", data.getTypeAnnotations()[0]); + } + + @Test + public void shouldBeAbleToDeserializeAnnotatedNullList() throws Exception { + IonValue ion = ion("foo::null.list"); + + IonValue data = ION_VALUE_MAPPER.parse(ion, IonValue.class); + + assertEquals(ion, data); + assertEquals(1, data.getTypeAnnotations().length); + assertEquals("foo", data.getTypeAnnotations()[0]); + } + + @Test + public void shouldBeAbleToSerializeAndDeserializePojo() throws Exception { + IonValueData source = new IonValueData(); + source.put("a", ion("1")); + source.put("c", ion("null.list")); + + IonValue data = ION_VALUE_MAPPER.serialize(source); + IonValueData result = ION_VALUE_MAPPER.parse(data, IonValueData.class); + + assertEquals(source, result); + } + + @Test + public void shouldBeAbleToSerializeAndDeserializeStringData() throws Exception { + StringData source = new StringData(); + source.put("a", "1"); + source.put("b", null); + + IonValue data = ION_VALUE_MAPPER.serialize(source); + StringData result = ION_VALUE_MAPPER.parse(data, StringData.class); + + assertEquals(source, result); + } + + @Test + public void shouldOverrideNullAccessPatternToBeDynamic() { + IonValueDeserializer deserializer = new IonValueDeserializer(); + assertEquals(AccessPattern.DYNAMIC, deserializer.getNullAccessPattern()); + } + + private static IonValue ion(String value) { + return SYSTEM.singleValue(value); + } +}