From cea184da52cf84f54cb8e14ff87149e8eed9a507 Mon Sep 17 00:00:00 2001 From: Mincong Huang Date: Sun, 7 Jun 2020 15:56:29 +0200 Subject: [PATCH] Collection serializers and deserializer should be contextual (#155) * Reproduce issue * ArraySerializer should be contextual * ArrayDeserializer should be contextual --- .../deserialize/ArrayDeserializer.java | 78 ++++++++++++++----- .../PriorityQueueDeserializer.java | 32 ++++++-- .../datatype/deserialize/SeqDeserializer.java | 47 ++++++----- .../datatype/deserialize/SetDeserializer.java | 33 ++++++-- .../deserialize/VavrDeserializers.java | 17 ++-- .../datatype/serialize/ArraySerializer.java | 33 ++++++-- .../datatype/serialize/ValueSerializer.java | 15 +++- .../datatype/serialize/VavrSerializers.java | 14 ++-- .../vavr/jackson/datatype/Issue154Test.java | 76 ++++++++++++++++++ .../jackson/datatype/PriorityQueueTest.java | 31 +++++++- .../vavr/jackson/datatype/seq/ArrayTest.java | 36 ++++++++- .../vavr/jackson/datatype/seq/ListTest.java | 28 +++++++ .../jackson/datatype/set/HashSetTest.java | 36 +++++++-- 13 files changed, 391 insertions(+), 85 deletions(-) create mode 100644 src/test/java/io/vavr/jackson/datatype/Issue154Test.java diff --git a/src/main/java/io/vavr/jackson/datatype/deserialize/ArrayDeserializer.java b/src/main/java/io/vavr/jackson/datatype/deserialize/ArrayDeserializer.java index bde9330..dc6c59f 100644 --- a/src/main/java/io/vavr/jackson/datatype/deserialize/ArrayDeserializer.java +++ b/src/main/java/io/vavr/jackson/datatype/deserialize/ArrayDeserializer.java @@ -21,10 +21,9 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import java.io.IOException; import java.util.ArrayList; @@ -34,33 +33,74 @@ import static com.fasterxml.jackson.core.JsonToken.END_ARRAY; import static com.fasterxml.jackson.core.JsonToken.VALUE_NULL; -abstract class ArrayDeserializer extends ValueDeserializer { +abstract class ArrayDeserializer extends ValueDeserializer implements ContextualDeserializer { private static final long serialVersionUID = 1L; - private final JavaType valueType; - private final boolean deserializeNullAsEmptyCollection; + protected final JavaType collectionType; + protected final JavaType elementType; + protected final TypeDeserializer elementTypeDeserializer; + protected final JsonDeserializer elementDeserializer; + protected final boolean deserializeNullAsEmptyCollection; - ArrayDeserializer(JavaType valueType, int typeCount, boolean deserializeNullAsEmptyCollection) { - super(valueType, typeCount); - this.valueType = valueType; + ArrayDeserializer(JavaType collectionType, int typeCount, JavaType elementType, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer, + boolean deserializeNullAsEmptyCollection) { + super(collectionType, typeCount); + this.collectionType = collectionType; + this.elementType = elementType; + this.elementTypeDeserializer = elementTypeDeserializer; + this.elementDeserializer = elementDeserializer; this.deserializeNullAsEmptyCollection = deserializeNullAsEmptyCollection; } abstract T create(List list, DeserializationContext ctxt) throws JsonMappingException; + /** + * Creates a new deserializer from the original one (this). + * + * @param elementTypeDeserializer the new deserializer for the element type + * @param elementDeserializer the new deserializer for the element itself + * @return a new deserializer + */ + abstract ArrayDeserializer createDeserializer(TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer); + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { + JsonDeserializer elementDeser = elementDeserializer; + TypeDeserializer elementTypeDeser = elementTypeDeserializer; + + if (elementDeser == null) { + elementDeser = ctxt.findContextualValueDeserializer(elementType, property); + } else { + elementDeser = ctxt.handleSecondaryContextualization(elementDeser, property, elementType); + } + if (elementTypeDeser != null) { + elementTypeDeser = elementTypeDeser.forProperty(property); + } + return createDeserializer(elementTypeDeser, elementDeser); + } + @Override - public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonDeserializer deserializer = deserializer(0); - List list = new ArrayList<>(); - if (!p.isExpectedStartArrayToken()) { - throw mappingException(ctxt, valueType.getRawClass(), p.getCurrentToken()); + public T deserialize(JsonParser parser, DeserializationContext context) throws IOException { + if (!parser.isExpectedStartArrayToken()) { + throw mappingException(context, collectionType.getRawClass(), parser.getCurrentToken()); } - for (JsonToken jsonToken = p.nextToken(); jsonToken != END_ARRAY; jsonToken = p.nextToken()) { - Object value = (jsonToken != VALUE_NULL) ? deserializer.deserialize(p, ctxt) : deserializer.getNullValue(ctxt); - list.add(value); + + List elements = new ArrayList<>(); + for (JsonToken jsonToken = parser.nextToken(); jsonToken != END_ARRAY; jsonToken = parser.nextToken()) { + Object element; + if (jsonToken == VALUE_NULL) { + element = elementDeserializer.getNullValue(context); + } else if (elementTypeDeserializer == null) { + element = elementDeserializer.deserialize(parser, context); + } else { + element = elementDeserializer.deserializeWithType(parser, context, elementTypeDeserializer); + } + elements.add(element); } - return create(list, ctxt); + return create(elements, context); } @Override diff --git a/src/main/java/io/vavr/jackson/datatype/deserialize/PriorityQueueDeserializer.java b/src/main/java/io/vavr/jackson/datatype/deserialize/PriorityQueueDeserializer.java index 3234c18..aad0c27 100644 --- a/src/main/java/io/vavr/jackson/datatype/deserialize/PriorityQueueDeserializer.java +++ b/src/main/java/io/vavr/jackson/datatype/deserialize/PriorityQueueDeserializer.java @@ -19,9 +19,8 @@ */ package io.vavr.jackson.datatype.deserialize; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import io.vavr.collection.PriorityQueue; import java.io.Serializable; @@ -32,17 +31,34 @@ class PriorityQueueDeserializer extends ArrayDeserializer> { private static final long serialVersionUID = 1L; - private final JavaType javaType; + PriorityQueueDeserializer(JavaType collectionType, JavaType elementType, TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer, boolean deserializeNullAsEmptyCollection) { + super(collectionType, 1, elementType, elementTypeDeserializer, elementDeserializer, deserializeNullAsEmptyCollection); + } - PriorityQueueDeserializer(JavaType valueType, boolean deserializeNullAsEmptyCollection) { - super(valueType, 1, deserializeNullAsEmptyCollection); - javaType = valueType; + /** + * Creates a new deserializer from the original one. + * + * @param origin the original deserializer + * @param elementTypeDeserializer the new deserializer for the element type + * @param elementDeserializer the new deserializer for the element itself + */ + PriorityQueueDeserializer(PriorityQueueDeserializer origin, TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer) { + this(origin.collectionType, origin.elementType, elementTypeDeserializer, elementDeserializer, + origin.deserializeNullAsEmptyCollection); } @SuppressWarnings("unchecked") @Override PriorityQueue create(List list, DeserializationContext ctxt) throws JsonMappingException { - checkContainedTypeIsComparable(ctxt, javaType.containedTypeOrUnknown(0)); + checkContainedTypeIsComparable(ctxt, collectionType.containedTypeOrUnknown(0)); return PriorityQueue.ofAll((Comparator & Serializable) (o1, o2) -> ((Comparable) o1).compareTo(o2), list); } + + @Override + PriorityQueueDeserializer createDeserializer(TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) { + return new PriorityQueueDeserializer(this, elementTypeDeserializer, elementDeserializer); + } + } diff --git a/src/main/java/io/vavr/jackson/datatype/deserialize/SeqDeserializer.java b/src/main/java/io/vavr/jackson/datatype/deserialize/SeqDeserializer.java index 0ef3bc5..bd44141 100644 --- a/src/main/java/io/vavr/jackson/datatype/deserialize/SeqDeserializer.java +++ b/src/main/java/io/vavr/jackson/datatype/deserialize/SeqDeserializer.java @@ -19,15 +19,9 @@ */ package io.vavr.jackson.datatype.deserialize; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonMappingException; -import io.vavr.collection.Array; -import io.vavr.collection.IndexedSeq; -import io.vavr.collection.Queue; -import io.vavr.collection.Seq; -import io.vavr.collection.Stream; -import io.vavr.collection.Vector; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import io.vavr.collection.*; import java.util.List; @@ -35,31 +29,48 @@ class SeqDeserializer extends ArrayDeserializer> { private static final long serialVersionUID = 1L; - private final JavaType javaType; + SeqDeserializer(JavaType collectionType, JavaType elementType, TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer, boolean deserializeNullAsEmptyCollection) { + super(collectionType, 1, elementType, elementTypeDeserializer, elementDeserializer, deserializeNullAsEmptyCollection); + } - SeqDeserializer(JavaType valueType, boolean deserializeNullAsEmptyCollection) { - super(valueType, 1, deserializeNullAsEmptyCollection); - javaType = valueType; + /** + * Creates a new deserializer from the original one. + * + * @param origin the original deserializer + * @param elementTypeDeserializer the new deserializer for the element type + * @param elementDeserializer the new deserializer for the element itself + */ + private SeqDeserializer(SeqDeserializer origin, TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer) { + this(origin.collectionType, origin.elementType, elementTypeDeserializer, elementDeserializer, + origin.deserializeNullAsEmptyCollection); } @Override Seq create(List result, DeserializationContext ctxt) throws JsonMappingException { - if (Array.class.isAssignableFrom(javaType.getRawClass())) { + if (Array.class.isAssignableFrom(collectionType.getRawClass())) { return Array.ofAll(result); } - if (Queue.class.isAssignableFrom(javaType.getRawClass())) { + if (Queue.class.isAssignableFrom(collectionType.getRawClass())) { return Queue.ofAll(result); } - if (Stream.class.isAssignableFrom(javaType.getRawClass())) { + if (Stream.class.isAssignableFrom(collectionType.getRawClass())) { return Stream.ofAll(result); } - if (Vector.class.isAssignableFrom(javaType.getRawClass())) { + if (Vector.class.isAssignableFrom(collectionType.getRawClass())) { return Vector.ofAll(result); } - if (IndexedSeq.class.isAssignableFrom(javaType.getRawClass())) { + if (IndexedSeq.class.isAssignableFrom(collectionType.getRawClass())) { return Array.ofAll(result); } // default deserialization [...] -> Seq return io.vavr.collection.List.ofAll(result); } + + @Override + SeqDeserializer createDeserializer(TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) { + return new SeqDeserializer(this, elementTypeDeserializer, elementDeserializer); + } + } diff --git a/src/main/java/io/vavr/jackson/datatype/deserialize/SetDeserializer.java b/src/main/java/io/vavr/jackson/datatype/deserialize/SetDeserializer.java index 979416e..c5f2b4e 100644 --- a/src/main/java/io/vavr/jackson/datatype/deserialize/SetDeserializer.java +++ b/src/main/java/io/vavr/jackson/datatype/deserialize/SetDeserializer.java @@ -21,7 +21,9 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import io.vavr.collection.HashSet; import io.vavr.collection.Set; @@ -33,24 +35,41 @@ class SetDeserializer extends ArrayDeserializer> { private static final long serialVersionUID = 1L; - private final JavaType javaType; + SetDeserializer(JavaType collectionType, JavaType elementType, TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer, boolean deserializeNullAsEmptyCollection) { + super(collectionType, 1, elementType, elementTypeDeserializer, elementDeserializer, deserializeNullAsEmptyCollection); + } - SetDeserializer(JavaType valueType, boolean deserializeNullAsEmptyCollection) { - super(valueType, 1, deserializeNullAsEmptyCollection); - javaType = valueType; + /** + * Creates a new deserializer from the original one. + * + * @param origin the original deserializer + * @param elementTypeDeserializer the new deserializer for the element type + * @param elementDeserializer the new deserializer for the element itself + */ + private SetDeserializer(SetDeserializer origin, TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer) { + this(origin.collectionType, origin.elementType, elementTypeDeserializer, elementDeserializer, + origin.deserializeNullAsEmptyCollection); } @SuppressWarnings("unchecked") @Override Set create(List result, DeserializationContext ctx) throws JsonMappingException { - if (io.vavr.collection.SortedSet.class.isAssignableFrom(javaType.getRawClass())) { - checkContainedTypeIsComparable(ctx, javaType.containedTypeOrUnknown(0)); + if (io.vavr.collection.SortedSet.class.isAssignableFrom(collectionType.getRawClass())) { + checkContainedTypeIsComparable(ctx, collectionType.containedTypeOrUnknown(0)); return io.vavr.collection.TreeSet.ofAll((Comparator & Serializable) (o1, o2) -> ((Comparable) o1).compareTo(o2), result); } - if (io.vavr.collection.LinkedHashSet.class.isAssignableFrom(javaType.getRawClass())) { + if (io.vavr.collection.LinkedHashSet.class.isAssignableFrom(collectionType.getRawClass())) { return io.vavr.collection.LinkedHashSet.ofAll(result); } // default deserialization [...] -> Set return HashSet.ofAll(result); } + + @Override + SetDeserializer createDeserializer(TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) { + return new SetDeserializer(this, elementTypeDeserializer, elementDeserializer); + } + } diff --git a/src/main/java/io/vavr/jackson/datatype/deserialize/VavrDeserializers.java b/src/main/java/io/vavr/jackson/datatype/deserialize/VavrDeserializers.java index 8d1bd45..1c10ca7 100644 --- a/src/main/java/io/vavr/jackson/datatype/deserialize/VavrDeserializers.java +++ b/src/main/java/io/vavr/jackson/datatype/deserialize/VavrDeserializers.java @@ -152,25 +152,28 @@ public JsonDeserializer findReferenceDeserializer(ReferenceType type, } @Override - public JsonDeserializer findCollectionLikeDeserializer(CollectionLikeType type, + public JsonDeserializer findCollectionLikeDeserializer(CollectionLikeType collectionType, DeserializationConfig config, BeanDescription beanDesc, TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) throws JsonMappingException { - Class raw = type.getRawClass(); + Class raw = collectionType.getRawClass(); if (raw == CharSeq.class) { - return new CharSeqDeserializer(type); + return new CharSeqDeserializer(collectionType); } if (Seq.class.isAssignableFrom(raw)) { - return new SeqDeserializer(type, settings.deserializeNullAsEmptyCollection()); + return new SeqDeserializer(collectionType, collectionType.getContentType(), elementTypeDeserializer, + elementDeserializer, settings.deserializeNullAsEmptyCollection()); } if (Set.class.isAssignableFrom(raw)) { - return new SetDeserializer(type, settings.deserializeNullAsEmptyCollection()); + return new SetDeserializer(collectionType, collectionType.getContentType(), elementTypeDeserializer, + elementDeserializer, settings.deserializeNullAsEmptyCollection()); } if (PriorityQueue.class.isAssignableFrom(raw)) { - return new PriorityQueueDeserializer(type, settings.deserializeNullAsEmptyCollection()); + return new PriorityQueueDeserializer(collectionType, collectionType.getContentType(), + elementTypeDeserializer, elementDeserializer, settings.deserializeNullAsEmptyCollection()); } - return super.findCollectionLikeDeserializer(type, config, beanDesc, elementTypeDeserializer, elementDeserializer); + return super.findCollectionLikeDeserializer(collectionType, config, beanDesc, elementTypeDeserializer, elementDeserializer); } @Override diff --git a/src/main/java/io/vavr/jackson/datatype/serialize/ArraySerializer.java b/src/main/java/io/vavr/jackson/datatype/serialize/ArraySerializer.java index 1822bff..635b8e8 100644 --- a/src/main/java/io/vavr/jackson/datatype/serialize/ArraySerializer.java +++ b/src/main/java/io/vavr/jackson/datatype/serialize/ArraySerializer.java @@ -19,8 +19,8 @@ */ package io.vavr.jackson.datatype.serialize; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.type.CollectionLikeType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.vavr.Value; @@ -28,12 +28,26 @@ import java.io.IOException; import java.util.ArrayList; -class ArraySerializer> extends ValueSerializer { +class ArraySerializer> extends ValueSerializer implements ContextualSerializer { private static final long serialVersionUID = 1L; - ArraySerializer(JavaType type) { - super(type); + ArraySerializer(JavaType collectionType, BeanProperty property) { + super(collectionType, property); + } + + ArraySerializer(JavaType collectionType) { + this(collectionType, null); + } + + /** + * Creates a new serializer from the original one. + * + * @param origin the original serializer + * @param property the new bean property + */ + ArraySerializer(ArraySerializer origin, BeanProperty property) { + this(origin.type, property); } @Override @@ -51,4 +65,13 @@ JavaType emulatedJavaType(JavaType type, TypeFactory typeFactory) { public boolean isEmpty(SerializerProvider provider, T value) { return value.isEmpty(); } + + @Override + public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) + throws JsonMappingException { + if (property == beanProperty) { + return this; + } + return new ArraySerializer<>(this, property); + } } diff --git a/src/main/java/io/vavr/jackson/datatype/serialize/ValueSerializer.java b/src/main/java/io/vavr/jackson/datatype/serialize/ValueSerializer.java index 6887d84..e00d3fa 100644 --- a/src/main/java/io/vavr/jackson/datatype/serialize/ValueSerializer.java +++ b/src/main/java/io/vavr/jackson/datatype/serialize/ValueSerializer.java @@ -20,6 +20,7 @@ package io.vavr.jackson.datatype.serialize; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; @@ -33,11 +34,17 @@ abstract class ValueSerializer extends StdSerializer { private static final long serialVersionUID = 1L; - JavaType type; + final JavaType type; + final BeanProperty beanProperty; ValueSerializer(JavaType type) { + this(type, null); + } + + ValueSerializer(JavaType type, BeanProperty property) { super(type); this.type = type; + this.beanProperty = property; } abstract Object toJavaObj(T value) throws IOException; @@ -53,12 +60,12 @@ public void serialize(T value, JsonGenerator gen, SerializerProvider provider) t try { JavaType emulated = emulatedJavaType(type, provider.getTypeFactory()); if (emulated.getRawClass() != Object.class) { - ser = provider.findTypedValueSerializer(emulated, true, null); + ser = provider.findTypedValueSerializer(emulated, true, beanProperty); } else { - ser = provider.findTypedValueSerializer(obj.getClass(), true, null); + ser = provider.findTypedValueSerializer(obj.getClass(), true, beanProperty); } } catch (Exception ignore) { - ser = provider.findTypedValueSerializer(obj.getClass(), true, null); + ser = provider.findTypedValueSerializer(obj.getClass(), true, beanProperty); } ser.serialize(obj, gen, provider); } diff --git a/src/main/java/io/vavr/jackson/datatype/serialize/VavrSerializers.java b/src/main/java/io/vavr/jackson/datatype/serialize/VavrSerializers.java index 9148b71..807fcd7 100644 --- a/src/main/java/io/vavr/jackson/datatype/serialize/VavrSerializers.java +++ b/src/main/java/io/vavr/jackson/datatype/serialize/VavrSerializers.java @@ -155,22 +155,22 @@ public JsonSerializer findReferenceSerializer(SerializationConfig config, @Override public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, - CollectionLikeType type, BeanDescription beanDesc, + CollectionLikeType collectionType, BeanDescription beanDesc, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { - Class raw = type.getRawClass(); + Class raw = collectionType.getRawClass(); if (raw == CharSeq.class) { - return new CharSeqSerializer(type); + return new CharSeqSerializer(collectionType); } if (Seq.class.isAssignableFrom(raw)) { - return new ArraySerializer<>(type); + return new ArraySerializer<>(collectionType); } if (Set.class.isAssignableFrom(raw)) { - return new ArraySerializer<>(type); + return new ArraySerializer<>(collectionType); } if (PriorityQueue.class.isAssignableFrom(raw)) { - return new ArraySerializer<>(type); + return new ArraySerializer<>(collectionType); } - return super.findCollectionLikeSerializer(config, type, beanDesc, elementTypeSerializer, elementValueSerializer); + return super.findCollectionLikeSerializer(config, collectionType, beanDesc, elementTypeSerializer, elementValueSerializer); } @Override diff --git a/src/test/java/io/vavr/jackson/datatype/Issue154Test.java b/src/test/java/io/vavr/jackson/datatype/Issue154Test.java new file mode 100644 index 0000000..99ea3e0 --- /dev/null +++ b/src/test/java/io/vavr/jackson/datatype/Issue154Test.java @@ -0,0 +1,76 @@ +package io.vavr.jackson.datatype; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.vavr.collection.List; +import org.junit.jupiter.api.Test; + +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Serialize of List of Date does not follow pattern defined in {@code @JsonFormat} + * https://github.com/vavr-io/vavr-jackson/issues/154 + */ +public class Issue154Test { + + private static class MyVavrClass { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Europe/Paris") + private List dates; + } + + private static class MyJavaClass { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Europe/Paris") + private java.util.List dates; + } + + @Test + void itShouldSerializeVavrListWithVavrModule() throws Exception { + MyVavrClass myClass = new MyVavrClass(); + myClass.dates = List.of(new Date(1591221600000L), new Date(1591308000000L)); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new VavrModule()); + + String json = mapper.writeValueAsString(myClass); + assertEquals("{\"dates\":[\"2020-06-04\",\"2020-06-05\"]}", json); + } + + @Test + void itShouldSerializeVavrListWithVavrModuleAndJavaTimeModule() throws Exception { + MyVavrClass myClass = new MyVavrClass(); + myClass.dates = List.of(new Date(1591221600000L), new Date(1591308000000L)); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new VavrModule()); + mapper.registerModule(new JavaTimeModule()); + + String json = mapper.writeValueAsString(myClass); + assertEquals("{\"dates\":[\"2020-06-04\",\"2020-06-05\"]}", json); + } + + @Test + void itShouldSerializeJavaListWithJavaTimeModule() throws Exception { + MyJavaClass myClass = new MyJavaClass(); + myClass.dates = List.of(new Date(1591221600000L), new Date(1591308000000L)).asJava(); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + + String json = mapper.writeValueAsString(myClass); + assertEquals("{\"dates\":[\"2020-06-04\",\"2020-06-05\"]}", json); + } + + @Test + void itShouldSerializeJavaListWithoutModule() throws Exception { + MyJavaClass myClass = new MyJavaClass(); + myClass.dates = List.of(new Date(1591221600000L), new Date(1591308000000L)).asJava(); + + ObjectMapper mapper = new ObjectMapper(); + + String json = mapper.writeValueAsString(myClass); + assertEquals("{\"dates\":[\"2020-06-04\",\"2020-06-05\"]}", json); + } +} diff --git a/src/test/java/io/vavr/jackson/datatype/PriorityQueueTest.java b/src/test/java/io/vavr/jackson/datatype/PriorityQueueTest.java index 49e513e..35512a5 100644 --- a/src/test/java/io/vavr/jackson/datatype/PriorityQueueTest.java +++ b/src/test/java/io/vavr/jackson/datatype/PriorityQueueTest.java @@ -1,7 +1,6 @@ package io.vavr.jackson.datatype; -import static org.junit.jupiter.api.Assertions.assertThrows; - +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; @@ -9,6 +8,7 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.vavr.collection.PriorityQueue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -18,6 +18,9 @@ import javax.xml.bind.annotation.XmlRootElement; import java.io.IOException; import java.util.Arrays; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class PriorityQueueTest extends BaseTest { @@ -131,4 +134,28 @@ void testXmlSerialization() throws IOException { XmlSerializeVavr restored = mapper.readValue(javaUtilValue, XmlSerializeVavr.class); Assertions.assertEquals(restored.transitTypes.size(), 3); } + + static class FrenchDates { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy", timezone = "Europe/Paris") + PriorityQueue dates; + } + + @Test + void testSerializeWithContext() throws IOException { + // Given an object containing dates to serialize + FrenchDates src = new FrenchDates(); + src.dates = PriorityQueue.of(new Date(1591308000000L)); + + // When serializing them using object mapper + // with VAVR module and Java Time module + ObjectMapper mapper = mapper().registerModule(new JavaTimeModule()); + String json = mapper.writeValueAsString(src); + + // Then the serialization is successful + Assertions.assertEquals("{\"dates\":[\"05/06/2020\"]}", json); + + // And the deserialization is successful + FrenchDates restored = mapper.readValue(json, FrenchDates.class); + Assertions.assertEquals(src.dates, restored.dates); + } } diff --git a/src/test/java/io/vavr/jackson/datatype/seq/ArrayTest.java b/src/test/java/io/vavr/jackson/datatype/seq/ArrayTest.java index 66521cc..5143666 100644 --- a/src/test/java/io/vavr/jackson/datatype/seq/ArrayTest.java +++ b/src/test/java/io/vavr/jackson/datatype/seq/ArrayTest.java @@ -1,12 +1,18 @@ package io.vavr.jackson.datatype.seq; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.type.TypeReference; - -import java.util.Arrays; - +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.vavr.collection.Array; import io.vavr.collection.Seq; import io.vavr.control.Option; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; public class ArrayTest extends SeqTest { @Override @@ -23,4 +29,28 @@ protected TypeReference>> typeReferenceWithOption() { protected Seq of(Object... objects) { return Array.ofAll(Arrays.asList(objects)); } + + static class FrenchDates { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy", timezone = "Europe/Paris") + Array dates; + } + + @Test + void testSerializeWithContext() throws IOException { + // Given an object containing dates to serialize + FrenchDates src = new FrenchDates(); + src.dates = Array.of(new Date(1591308000000L)); + + // When serializing them using object mapper + // with VAVR module and Java Time module + ObjectMapper mapper = mapper().registerModule(new JavaTimeModule()); + String json = mapper.writeValueAsString(src); + + // Then the serialization is successful + Assertions.assertEquals("{\"dates\":[\"05/06/2020\"]}", json); + + // And the deserialization is successful + FrenchDates restored = mapper.readValue(json, FrenchDates.class); + Assertions.assertEquals(src.dates, restored.dates); + } } diff --git a/src/test/java/io/vavr/jackson/datatype/seq/ListTest.java b/src/test/java/io/vavr/jackson/datatype/seq/ListTest.java index 1f7ed9b..2058755 100644 --- a/src/test/java/io/vavr/jackson/datatype/seq/ListTest.java +++ b/src/test/java/io/vavr/jackson/datatype/seq/ListTest.java @@ -1,8 +1,11 @@ package io.vavr.jackson.datatype.seq; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.vavr.collection.List; import io.vavr.collection.Seq; import io.vavr.control.Option; @@ -11,6 +14,7 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Date; public class ListTest extends SeqTest { @@ -64,4 +68,28 @@ void testJsonTypeInfo() throws IOException { Assertions.assertEquals("hello", restored.f.head().type); } + static class FrenchDates { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy", timezone = "Europe/Paris") + List dates; + } + + @Test + void testSerializeWithContext() throws IOException { + // Given an object containing dates to serialize + FrenchDates src = new FrenchDates(); + src.dates = List.of(new Date(1591308000000L)); + + // When serializing them using object mapper + // with VAVR module and Java Time module + ObjectMapper mapper = mapper().registerModule(new JavaTimeModule()); + String json = mapper.writeValueAsString(src); + + // Then the serialization is successful + Assertions.assertEquals("{\"dates\":[\"05/06/2020\"]}", json); + + // And the deserialization is successful + FrenchDates restored = mapper.readValue(json, FrenchDates.class); + Assertions.assertEquals(src.dates, restored.dates); + } + } diff --git a/src/test/java/io/vavr/jackson/datatype/set/HashSetTest.java b/src/test/java/io/vavr/jackson/datatype/set/HashSetTest.java index b11c692..9d7175f 100644 --- a/src/test/java/io/vavr/jackson/datatype/set/HashSetTest.java +++ b/src/test/java/io/vavr/jackson/datatype/set/HashSetTest.java @@ -1,16 +1,18 @@ package io.vavr.jackson.datatype.set; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.type.TypeReference; - +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.vavr.collection.HashSet; +import io.vavr.collection.Set; +import io.vavr.control.Option; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Arrays; - -import io.vavr.collection.HashSet; -import io.vavr.collection.Set; -import io.vavr.control.Option; +import java.util.Date; public class HashSetTest extends SetTest { @@ -38,4 +40,28 @@ protected Set of(Object... objects) { void testDefaultDeserialization() throws IOException { Assertions.assertEquals(mapper().readValue("[1]", Set.class), HashSet.of(1)); } + + static class FrenchDates { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy", timezone = "Europe/Paris") + Set dates; + } + + @Test + void testSerializeWithContext() throws IOException { + // Given an object containing dates to serialize + FrenchDates src = new FrenchDates(); + src.dates = HashSet.of(new Date(1591308000000L)); + + // When serializing them using object mapper + // with VAVR module and Java Time module + ObjectMapper mapper = mapper().registerModule(new JavaTimeModule()); + String json = mapper.writeValueAsString(src); + + // Then the serialization is successful + Assertions.assertEquals("{\"dates\":[\"05/06/2020\"]}", json); + + // And the deserialization is successful + FrenchDates restored = mapper.readValue(json, FrenchDates.class); + Assertions.assertEquals(src.dates, restored.dates); + } }