vavr-io / vavr-jackson

Jackson datatype module for Vavr
Apache License 2.0
97 stars 35 forks source link

Serializer and deserializer of Map and Multimap should be contextual #158

Closed mincong-h closed 4 years ago

mincong-h commented 4 years ago

Overview

Part of #157 , this PR makes the serialization and deserialization contextual for VAVR Map and Multimap. Let's see a concrete example before going further:

@JsonProperty
@JsonSerialize(
    keyUsing = CustomKeySerializer.class,
    contentUsing = CustomValueSerializer.class
)
public Map<CustomKey, CustomValue> getMap() {
    return map;
}

Sometimes, users may want to use a custom serializer or deserializer for their key or content of their map. This is now possible thanks to contextualization. Now, let's see how this is done in Map and Multimap.

Map Serialization

Map serialization emulates VAVR Map to Java LinkedHashMap. To support the contextualization, saving the bean property is enough.

Multimap Serialization

Multimap serialization emulates VAVR Map to Java Map as LinkedHashMap<K, ArrayList<V>>. To support the contextualization, saving the bean property as done in map serialization. However, we have more bindings in the Java type than before, so using magic number to get the contained K, V via type.containedType(...) does not work anymore. Instead, using mapType.getKeyType() and mapType.getContentType() work.

Map/Multimap Deserialization

For map-like deserialization, the changes are similar to previous PRs where we save 3 additional attributes to deserializer: 1) key deserializer 2) type deserializer of the element 3) JSON deserializer of the element. These 3 attributes may be changed during contextualization, since a new deserializer can be found by Jackson after the lookup. As you can see, these three attributes are now marked as final. Meaning that if anything is modified, we create a new deserializer by copying the information from the existing one. Since the deserializer are handled by contextualization, I removed the logic from resolution (ResolvableDeserializer#resolve(...)) since it's redundant.

Additional Notes

The changes of this PR is mainly inspired by Jackson Databind 2.7, the version used by VAVR-Jackson. https://github.com/FasterXML/jackson-databind/blob/2.7/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java

Different from Map, Multimap does not support contentUsing, because the content of Multimap<K, V> is not V but multiple Vs. So defining a custom serializer and deserializer via contentUsing= for V is not the right way to do. The workaround for custom-value-type serialization is:

Multimap<CustomKey, CustomElement> map;
    @JsonSerialize(using = CustomElementSerializer.class)
    @JsonDeserialize(using = CustomElementDeserializer.class)
    static class CustomElement {
        private final String value;

        CustomElement(String value) {
            this.value = value;
        }
    }

See more detail in MultimapTest#testContextualizationOfKeyAndElement.

mincong-h commented 4 years ago

It seems that the tests worked in Jackson 2.7.2 but failed in 2.8.4 and 2.9.1... 🤦 I will fix them.

codecov-commenter commented 4 years ago

Codecov Report

Merging #158 into master will increase coverage by 0.04%. The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #158      +/-   ##
============================================
+ Coverage     93.21%   93.26%   +0.04%     
- Complexity      323      339      +16     
============================================
  Files            47       47              
  Lines           752      787      +35     
  Branches        168      173       +5     
============================================
+ Hits            701      734      +33     
- Misses           22       24       +2     
  Partials         29       29              
Impacted Files Coverage Δ Complexity Δ
.../jackson/datatype/deserialize/MapDeserializer.java 100.00% <100.00%> (ø) 9.00 <3.00> (+3.00)
...kson/datatype/deserialize/MaplikeDeserializer.java 100.00% <100.00%> (ø) 10.00 <8.00> (+5.00)
...son/datatype/deserialize/MultimapDeserializer.java 100.00% <100.00%> (ø) 9.00 <3.00> (+2.00)
...ackson/datatype/deserialize/VavrDeserializers.java 93.02% <100.00%> (ø) 38.00 <0.00> (ø)
...vavr/jackson/datatype/serialize/MapSerializer.java 100.00% <100.00%> (ø) 7.00 <4.00> (+3.00)
...jackson/datatype/serialize/MultimapSerializer.java 100.00% <100.00%> (ø) 9.00 <5.00> (+3.00)
...vr/jackson/datatype/serialize/ValueSerializer.java 72.72% <0.00%> (-9.10%) 4.00% <0.00%> (ø%)

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 0b6eec8...ef982be. Read the comment docs.

mincong-h commented 4 years ago

@ruslansennov would you have some time to review it? I would like to merge it today or tomorrow because I want to do some post-PR clean-up. Or maybe I can merge it and if you find anything unexpected, I can send another PR to fix it later.

ruslansennov commented 4 years ago

@mincong-h sorry, but I do not have time to do anything, feel free to merge it

mincong-h commented 4 years ago

@ruslansennov , thanks for you reply. No problem, I will handle it tonight then. 👍