Mapping FieldValueIDs for the payload of the Emarsys API (3 Part Series)
1 Create a custom Symfony Normalizer and Denormalizer for mapping values
2 Create a custom JMS Serializer handler for mapping values
3 Create a custom Jackson JsonSerializer und JsonDeserializer for mapping values
For my series of articles, I also wanted to see how this requirement to mapping values could be implemented with Jackson.
The first paragraph “The requirements and history” from the first article describes the requirements for Emarsys to rewrite the values for the payload.
The required packages
- com.fasterxml.jackson.core:jackson-databin
- com.fasterxml.jackson.datatype:jackson-datatype-jsr310
See the pom.xml in the example for the latest versions.
Minimal structure of a custom JsonSerializer and JsonDeserializer
To solve the requirements to map the values for Emarsys, a custom JsonSerializer and JsonDeserializer is needed. I call these MappingValueSerializer and MappingValueDeserializer.
Below is the minimal structure of a custom MappingValueSerializer
and MappingValueDeserializer
:
<span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span><span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span><span>private</span> <span>String</span> <span>fieldName</span><span>;</span><span>public</span> <span>class</span> <span>MappingValueSerializer</span> <span>extends</span> <span>JsonSerializer</span><span><</span><span>String</span><span>></span> <span>{</span><span>@Override</span><span>public</span> <span>void</span> <span>serialize</span><span>(</span><span>String</span> <span>value</span><span>,</span> <span>JsonGenerator</span> <span>jsonGenerator</span><span>,</span> <span>SerializerProvider</span> <span>serializerProvider</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span><span>jsonGenerator</span><span>.</span><span>writeString</span><span>(</span><span>"serialized: "</span> <span>+</span> <span>value</span><span>);</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>MappingValueDeserializer</span> <span>extends</span> <span>JsonDeserializer</span><span><</span><span>String</span><span>></span> <span>{</span><span>@Override</span><span>public</span> <span>String</span> <span>deserialize</span><span>(</span><span>JsonParser</span> <span>jsonParser</span><span>,</span> <span>DeserializationContext</span> <span>deserializationContext</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span><span>String</span> <span>value</span> <span>=</span> <span>jsonParser</span><span>.</span><span>getText</span><span>();</span><span>return</span> <span>"deserialized: "</span> <span>+</span> <span>value</span><span>;</span><span>}</span><span>}</span><span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span> <span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span> <span>private</span> <span>String</span> <span>fieldName</span><span>;</span> <span>public</span> <span>class</span> <span>MappingValueSerializer</span> <span>extends</span> <span>JsonSerializer</span><span><</span><span>String</span><span>></span> <span>{</span> <span>@Override</span> <span>public</span> <span>void</span> <span>serialize</span><span>(</span><span>String</span> <span>value</span><span>,</span> <span>JsonGenerator</span> <span>jsonGenerator</span><span>,</span> <span>SerializerProvider</span> <span>serializerProvider</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span> <span>jsonGenerator</span><span>.</span><span>writeString</span><span>(</span><span>"serialized: "</span> <span>+</span> <span>value</span><span>);</span> <span>}</span> <span>}</span> <span>public</span> <span>class</span> <span>MappingValueDeserializer</span> <span>extends</span> <span>JsonDeserializer</span><span><</span><span>String</span><span>></span> <span>{</span> <span>@Override</span> <span>public</span> <span>String</span> <span>deserialize</span><span>(</span><span>JsonParser</span> <span>jsonParser</span><span>,</span> <span>DeserializationContext</span> <span>deserializationContext</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span> <span>String</span> <span>value</span> <span>=</span> <span>jsonParser</span><span>.</span><span>getText</span><span>();</span> <span>return</span> <span>"deserialized: "</span> <span>+</span> <span>value</span><span>;</span> <span>}</span> <span>}</span>@JsonSerialize(using = MappingValueSerializer.class) @JsonDeserialize(using = MappingValueDeserializer.class) private String fieldName; public class MappingValueSerializer extends JsonSerializer<String> { @Override public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString("serialized: " + value); } } public class MappingValueDeserializer extends JsonDeserializer<String> { @Override public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { String value = jsonParser.getText(); return "deserialized: " + value; } }
Enter fullscreen mode Exit fullscreen mode
In ContactDto, the fields salutation
and marketingInformation
for which values have to be rewritten are defined.
Fields/Direction | serialize (T -> String) | deserialize (String -> T) |
---|---|---|
salutation | “FEMALE” -> “2” | “2” -> “FEMALE” |
marketingInformation | true -> “1” | “1” -> true |
For the serialize process it is the FieldValueID (String) and for the deserialize process the type String for salutation
and the type Boolean for marketingInformation
.
So if you want to do the mapping, you would need a JsonSerializer to write the FieldValueID (String) for salutation
and marketingInformation
and a JsonDeserializer to set the value for stalutation
(String) and marketingInformation
(Boolean).
Custom Type
However, I only want to have a JsonDeserializer that can process String, Boolean and in the future other types. For this purpose, I create my own type MappingValue<>
. Most importantly, I can transport all types with this custom generics.
<span>package</span> <span>com.microservice.crm.serializer</span><span>;</span><span>public</span> <span>class</span> <span>MappingValue</span><span><</span><span>T</span><span>></span> <span>{</span><span>T</span> <span>value</span><span>;</span><span>public</span> <span>MappingValue</span><span>(</span><span>T</span> <span>value</span><span>)</span> <span>{</span><span>this</span><span>.</span><span>value</span> <span>=</span> <span>value</span><span>;</span><span>}</span><span>public</span> <span>T</span> <span>getValue</span><span>()</span> <span>{</span><span>return</span> <span>this</span><span>.</span><span>value</span><span>;</span><span>}</span><span>}</span><span>package</span> <span>com.microservice.crm.serializer</span><span>;</span> <span>public</span> <span>class</span> <span>MappingValue</span><span><</span><span>T</span><span>></span> <span>{</span> <span>T</span> <span>value</span><span>;</span> <span>public</span> <span>MappingValue</span><span>(</span><span>T</span> <span>value</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>value</span> <span>=</span> <span>value</span><span>;</span> <span>}</span> <span>public</span> <span>T</span> <span>getValue</span><span>()</span> <span>{</span> <span>return</span> <span>this</span><span>.</span><span>value</span><span>;</span> <span>}</span> <span>}</span>package com.microservice.crm.serializer; public class MappingValue<T> { T value; public MappingValue(T value) { this.value = value; } public T getValue() { return this.value; } }
Enter fullscreen mode Exit fullscreen mode
ContactDto
First of all the complete ContactDto with all fields and annotations. I will explain the individual annotations below.
<span>package</span> <span>com.article.jackson.dto</span><span>;</span><span>import</span> <span>java.time.LocalDate</span><span>;</span><span>import</span> <span>com.article.jackson.annotation.MappingTable</span><span>;</span><span>import</span> <span>com.article.jackson.serializer.MappingValue</span><span>;</span><span>import</span> <span>com.article.jackson.serializer.MappingValueDeserializer</span><span>;</span><span>import</span> <span>com.article.jackson.serializer.MappingValueSerializer</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.annotation.JsonAutoDetect</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.annotation.JsonFormat</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.annotation.JsonIgnoreProperties</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.annotation.JsonInclude</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.annotation.JsonProperty</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.annotation.JsonDeserialize</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.annotation.JsonSerialize</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer</span><span>;</span><span>@JsonIgnoreProperties</span><span>(</span><span>ignoreUnknown</span> <span>=</span> <span>true</span><span>)</span><span>@JsonInclude</span><span>(</span><span>JsonInclude</span><span>.</span><span>Include</span><span>.</span><span>NON_NULL</span><span>)</span><span>@JsonAutoDetect</span><span>(</span><span>fieldVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>ANY</span><span>,</span><span>getterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span><span>setterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span><span>isGetterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>)</span><span>public</span> <span>class</span> <span>ContactDto</span> <span>{</span><span>@JsonProperty</span><span>(</span><span>"1"</span><span>)</span><span>private</span> <span>String</span> <span>firstname</span><span>;</span><span>@JsonProperty</span><span>(</span><span>"2"</span><span>)</span><span>private</span> <span>String</span> <span>lastname</span><span>;</span><span>@JsonProperty</span><span>(</span><span>"3"</span><span>)</span><span>private</span> <span>String</span> <span>email</span><span>;</span><span>@JsonProperty</span><span>(</span><span>"4"</span><span>)</span><span>@JsonFormat</span><span>(</span><span>pattern</span> <span>=</span> <span>"yyyy-MM-dd"</span><span>)</span><span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>LocalDateSerializer</span><span>.</span><span>class</span><span>)</span><span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>LocalDateDeserializer</span><span>.</span><span>class</span><span>)</span><span>private</span> <span>LocalDate</span> <span>birthday</span><span>;</span><span>@JsonProperty</span><span>(</span><span>"46"</span><span>)</span><span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>SALUTATION</span><span>)</span><span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span><span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span><span>private</span> <span>MappingValue</span><span><</span><span>String</span><span>></span> <span>salutation</span><span>;</span><span>@JsonProperty</span><span>(</span><span>"100674"</span><span>)</span><span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>MARKETING_INFORMATION</span><span>)</span><span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span><span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span><span>private</span> <span>MappingValue</span><span><</span><span>Boolean</span><span>></span> <span>marketingInformation</span><span>;</span><span>// other getter and setter</span><span>public</span> <span>String</span> <span>getSalutation</span><span>()</span> <span>{</span><span>return</span> <span>salutation</span><span>.</span><span>getValue</span><span>();</span><span>}</span><span>public</span> <span>void</span> <span>setSalutation</span><span>(</span><span>String</span> <span>salutation</span><span>)</span> <span>{</span><span>this</span><span>.</span><span>salutation</span> <span>=</span> <span>new</span> <span>MappingValue</span><span><>(</span><span>salutation</span><span>);</span><span>}</span><span>public</span> <span>Boolean</span> <span>getMarketingInformation</span><span>()</span> <span>{</span><span>return</span> <span>this</span><span>.</span><span>isMarketingInformation</span><span>();</span><span>}</span><span>public</span> <span>Boolean</span> <span>isMarketingInformation</span><span>()</span> <span>{</span><span>return</span> <span>marketingInformation</span><span>.</span><span>getValue</span><span>();</span><span>}</span><span>public</span> <span>void</span> <span>setMarketingInformation</span><span>(</span><span>Boolean</span> <span>marketingInformation</span><span>)</span> <span>{</span><span>this</span><span>.</span><span>marketingInformation</span> <span>=</span> <span>new</span> <span>MappingValue</span><span><>(</span><span>marketingInformation</span><span>);</span><span>}</span><span>}</span><span>package</span> <span>com.article.jackson.dto</span><span>;</span> <span>import</span> <span>java.time.LocalDate</span><span>;</span> <span>import</span> <span>com.article.jackson.annotation.MappingTable</span><span>;</span> <span>import</span> <span>com.article.jackson.serializer.MappingValue</span><span>;</span> <span>import</span> <span>com.article.jackson.serializer.MappingValueDeserializer</span><span>;</span> <span>import</span> <span>com.article.jackson.serializer.MappingValueSerializer</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.annotation.JsonAutoDetect</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.annotation.JsonFormat</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.annotation.JsonIgnoreProperties</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.annotation.JsonInclude</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.annotation.JsonProperty</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.annotation.JsonDeserialize</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.annotation.JsonSerialize</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer</span><span>;</span> <span>@JsonIgnoreProperties</span><span>(</span><span>ignoreUnknown</span> <span>=</span> <span>true</span><span>)</span> <span>@JsonInclude</span><span>(</span><span>JsonInclude</span><span>.</span><span>Include</span><span>.</span><span>NON_NULL</span><span>)</span> <span>@JsonAutoDetect</span><span>(</span> <span>fieldVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>ANY</span><span>,</span> <span>getterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span> <span>setterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span> <span>isGetterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span> <span>)</span> <span>public</span> <span>class</span> <span>ContactDto</span> <span>{</span> <span>@JsonProperty</span><span>(</span><span>"1"</span><span>)</span> <span>private</span> <span>String</span> <span>firstname</span><span>;</span> <span>@JsonProperty</span><span>(</span><span>"2"</span><span>)</span> <span>private</span> <span>String</span> <span>lastname</span><span>;</span> <span>@JsonProperty</span><span>(</span><span>"3"</span><span>)</span> <span>private</span> <span>String</span> <span>email</span><span>;</span> <span>@JsonProperty</span><span>(</span><span>"4"</span><span>)</span> <span>@JsonFormat</span><span>(</span><span>pattern</span> <span>=</span> <span>"yyyy-MM-dd"</span><span>)</span> <span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>LocalDateSerializer</span><span>.</span><span>class</span><span>)</span> <span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>LocalDateDeserializer</span><span>.</span><span>class</span><span>)</span> <span>private</span> <span>LocalDate</span> <span>birthday</span><span>;</span> <span>@JsonProperty</span><span>(</span><span>"46"</span><span>)</span> <span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>SALUTATION</span><span>)</span> <span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span> <span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span> <span>private</span> <span>MappingValue</span><span><</span><span>String</span><span>></span> <span>salutation</span><span>;</span> <span>@JsonProperty</span><span>(</span><span>"100674"</span><span>)</span> <span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>MARKETING_INFORMATION</span><span>)</span> <span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span> <span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span> <span>private</span> <span>MappingValue</span><span><</span><span>Boolean</span><span>></span> <span>marketingInformation</span><span>;</span> <span>// other getter and setter</span> <span>public</span> <span>String</span> <span>getSalutation</span><span>()</span> <span>{</span> <span>return</span> <span>salutation</span><span>.</span><span>getValue</span><span>();</span> <span>}</span> <span>public</span> <span>void</span> <span>setSalutation</span><span>(</span><span>String</span> <span>salutation</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>salutation</span> <span>=</span> <span>new</span> <span>MappingValue</span><span><>(</span><span>salutation</span><span>);</span> <span>}</span> <span>public</span> <span>Boolean</span> <span>getMarketingInformation</span><span>()</span> <span>{</span> <span>return</span> <span>this</span><span>.</span><span>isMarketingInformation</span><span>();</span> <span>}</span> <span>public</span> <span>Boolean</span> <span>isMarketingInformation</span><span>()</span> <span>{</span> <span>return</span> <span>marketingInformation</span><span>.</span><span>getValue</span><span>();</span> <span>}</span> <span>public</span> <span>void</span> <span>setMarketingInformation</span><span>(</span><span>Boolean</span> <span>marketingInformation</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>marketingInformation</span> <span>=</span> <span>new</span> <span>MappingValue</span><span><>(</span><span>marketingInformation</span><span>);</span> <span>}</span> <span>}</span>package com.article.jackson.dto; import java.time.LocalDate; import com.article.jackson.annotation.MappingTable; import com.article.jackson.serializer.MappingValue; import com.article.jackson.serializer.MappingValueDeserializer; import com.article.jackson.serializer.MappingValueSerializer; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE ) public class ContactDto { @JsonProperty("1") private String firstname; @JsonProperty("2") private String lastname; @JsonProperty("3") private String email; @JsonProperty("4") @JsonFormat(pattern = "yyyy-MM-dd") @JsonSerialize(using = LocalDateSerializer.class) @JsonDeserialize(using = LocalDateDeserializer.class) private LocalDate birthday; @JsonProperty("46") @MappingTable(map = Maps.SALUTATION) @JsonSerialize(using = MappingValueSerializer.class) @JsonDeserialize(using = MappingValueDeserializer.class) private MappingValue<String> salutation; @JsonProperty("100674") @MappingTable(map = Maps.MARKETING_INFORMATION) @JsonSerialize(using = MappingValueSerializer.class) @JsonDeserialize(using = MappingValueDeserializer.class) private MappingValue<Boolean> marketingInformation; // other getter and setter public String getSalutation() { return salutation.getValue(); } public void setSalutation(String salutation) { this.salutation = new MappingValue<>(salutation); } public Boolean getMarketingInformation() { return this.isMarketingInformation(); } public Boolean isMarketingInformation() { return marketingInformation.getValue(); } public void setMarketingInformation(Boolean marketingInformation) { this.marketingInformation = new MappingValue<>(marketingInformation); } }
Enter fullscreen mode Exit fullscreen mode
Custom Annotation @MappingTable and Enum Constants for the MappingTable
The MappingTable with the FieldValueIDs for salutation
and marketingInformation
must be available in the MappingValueSerializer and MappingValueDeserializer.
Annotation @MappingTable
For this I create a custom annotation @MappingTable
that will be read in the MappingValueSerializer and MappingValueDeserializer.
<span>package</span> <span>com.article.jackson.annotation</span><span>;</span><span>import</span> <span>java.lang.annotation.ElementType</span><span>;</span><span>import</span> <span>java.lang.annotation.Retention</span><span>;</span><span>import</span> <span>java.lang.annotation.RetentionPolicy</span><span>;</span><span>import</span> <span>java.lang.annotation.Target</span><span>;</span><span>import</span> <span>com.article.jackson.dto.Maps</span><span>;</span><span>@Retention</span><span>(</span><span>RetentionPolicy</span><span>.</span><span>RUNTIME</span><span>)</span><span>@Target</span><span>(</span><span>ElementType</span><span>.</span><span>FIELD</span><span>)</span><span>public</span> <span>@interface</span> <span>MappingTable</span> <span>{</span><span>Maps</span> <span>map</span><span>();</span><span>}</span><span>package</span> <span>com.article.jackson.annotation</span><span>;</span> <span>import</span> <span>java.lang.annotation.ElementType</span><span>;</span> <span>import</span> <span>java.lang.annotation.Retention</span><span>;</span> <span>import</span> <span>java.lang.annotation.RetentionPolicy</span><span>;</span> <span>import</span> <span>java.lang.annotation.Target</span><span>;</span> <span>import</span> <span>com.article.jackson.dto.Maps</span><span>;</span> <span>@Retention</span><span>(</span><span>RetentionPolicy</span><span>.</span><span>RUNTIME</span><span>)</span> <span>@Target</span><span>(</span><span>ElementType</span><span>.</span><span>FIELD</span><span>)</span> <span>public</span> <span>@interface</span> <span>MappingTable</span> <span>{</span> <span>Maps</span> <span>map</span><span>();</span> <span>}</span>package com.article.jackson.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.article.jackson.dto.Maps; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface MappingTable { Maps map(); }
Enter fullscreen mode Exit fullscreen mode
Enum Constants
<span>package</span> <span>com.article.jackson.dto</span><span>;</span><span>import</span> <span>java.util.Map</span><span>;</span><span>public</span> <span>enum</span> <span>Maps</span> <span>{</span><span>SALUTATION</span><span>(</span><span>Map</span><span>.</span><span>of</span><span>(</span><span>"1"</span><span>,</span> <span>"MALE"</span><span>,</span> <span>"2"</span><span>,</span> <span>"FEMALE"</span><span>,</span> <span>"6"</span><span>,</span> <span>"DIVERS"</span><span>)),</span><span>MARKETING_INFORMATION</span><span>(</span><span>Map</span><span>.</span><span>of</span><span>(</span><span>"1"</span><span>,</span> <span>true</span><span>,</span> <span>"2"</span><span>,</span> <span>false</span><span>));</span><span>private</span> <span>final</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>;</span><span>Maps</span><span>(</span><span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>)</span> <span>{</span><span>this</span><span>.</span><span>map</span> <span>=</span> <span>map</span><span>;</span><span>}</span><span>public</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>getMap</span><span>()</span> <span>{</span><span>return</span> <span>this</span><span>.</span><span>map</span><span>;</span><span>}</span><span>}</span><span>package</span> <span>com.article.jackson.dto</span><span>;</span> <span>import</span> <span>java.util.Map</span><span>;</span> <span>public</span> <span>enum</span> <span>Maps</span> <span>{</span> <span>SALUTATION</span><span>(</span><span>Map</span><span>.</span><span>of</span><span>(</span><span>"1"</span><span>,</span> <span>"MALE"</span><span>,</span> <span>"2"</span><span>,</span> <span>"FEMALE"</span><span>,</span> <span>"6"</span><span>,</span> <span>"DIVERS"</span><span>)),</span> <span>MARKETING_INFORMATION</span><span>(</span><span>Map</span><span>.</span><span>of</span><span>(</span><span>"1"</span><span>,</span> <span>true</span><span>,</span> <span>"2"</span><span>,</span> <span>false</span><span>));</span> <span>private</span> <span>final</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>;</span> <span>Maps</span><span>(</span><span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>map</span> <span>=</span> <span>map</span><span>;</span> <span>}</span> <span>public</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>getMap</span><span>()</span> <span>{</span> <span>return</span> <span>this</span><span>.</span><span>map</span><span>;</span> <span>}</span> <span>}</span>package com.article.jackson.dto; import java.util.Map; public enum Maps { SALUTATION(Map.of("1", "MALE", "2", "FEMALE", "6", "DIVERS")), MARKETING_INFORMATION(Map.of("1", true, "2", false)); private final Map<String, Object> map; Maps(Map<String, Object> map) { this.map = map; } public Map<String, Object> getMap() { return this.map; } }
Enter fullscreen mode Exit fullscreen mode
Field definitions
The enum constants Maps.SALUTATION
and Maps.MARKETING_INFORMATION
are referenced in the @MappingTable
annotation. The HashMaps are used in the JsonSerializer
and JsonDeserializer
for bi-directional mapping.
<span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>SALUTATION</span><span>)</span><span>private</span> <span>MappingValue</span><span><</span><span>String</span><span>></span> <span>salutation</span><span>;</span><span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>MARKETING_INFORMATION</span><span>)</span><span>private</span> <span>MappingValue</span><span><</span><span>Boolean</span><span>></span> <span>marketingInformation</span><span>;</span><span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>SALUTATION</span><span>)</span> <span>private</span> <span>MappingValue</span><span><</span><span>String</span><span>></span> <span>salutation</span><span>;</span> <span>@MappingTable</span><span>(</span><span>map</span> <span>=</span> <span>Maps</span><span>.</span><span>MARKETING_INFORMATION</span><span>)</span> <span>private</span> <span>MappingValue</span><span><</span><span>Boolean</span><span>></span> <span>marketingInformation</span><span>;</span>@MappingTable(map = Maps.SALUTATION) private MappingValue<String> salutation; @MappingTable(map = Maps.MARKETING_INFORMATION) private MappingValue<Boolean> marketingInformation;
Enter fullscreen mode Exit fullscreen mode
Reading and writing on the fields
The ObjectManager of Jackson writes and reads on the mutators (setter) and accessor (getter, isser) by default.
For the mutator and accessor of salutation
and marketingInformation
, however, I would like to define the type String or Boolean.
You can use an annotation to instruct Jackson to read and write only on the fields, so we can use the custom type MappingValue<> internally. The reading and writing process thus takes place on the fields and we can define String and Boolean for the mutator and accessor of salutation
and marketingInformation
.
<span>@JsonAutoDetect</span><span>(</span><span>fieldVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>ANY</span><span>,</span><span>getterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span><span>setterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span><span>isGetterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>)</span><span>@JsonAutoDetect</span><span>(</span> <span>fieldVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>ANY</span><span>,</span> <span>getterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span> <span>setterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span><span>,</span> <span>isGetterVisibility</span> <span>=</span> <span>JsonAutoDetect</span><span>.</span><span>Visibility</span><span>.</span><span>NONE</span> <span>)</span>@JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE )
Enter fullscreen mode Exit fullscreen mode
FieldIDs
The FieldIDs can be defined very easy with @JsonProperty
.
<span>@JsonProperty</span><span>(</span><span>"123"</span><span>)</span><span>@JsonProperty</span><span>(</span><span>"123"</span><span>)</span>@JsonProperty("123")
Enter fullscreen mode Exit fullscreen mode
Define custom JsonSerializer and JsonDeserializer
The custom JsonSerializer (MappingValueSerializer
) and JsonDeserializer (MappingValueDeserializer
) can be defined with @JsonSerialize
and @JsonDeserialize
on the field.
<span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span><span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span><span>@JsonSerialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueSerializer</span><span>.</span><span>class</span><span>)</span> <span>@JsonDeserialize</span><span>(</span><span>using</span> <span>=</span> <span>MappingValueDeserializer</span><span>.</span><span>class</span><span>)</span>@JsonSerialize(using = MappingValueSerializer.class) @JsonDeserialize(using = MappingValueDeserializer.class)
Enter fullscreen mode Exit fullscreen mode
Skip null values
Fields with null as value should not be serialized. This is because the fields that are sent are also updated. The annotation @JsonInclude
can be used for this.
<span>@JsonInclude</span><span>(</span><span>JsonInclude</span><span>.</span><span>Include</span><span>.</span><span>NON_NULL</span><span>)</span><span>@JsonInclude</span><span>(</span><span>JsonInclude</span><span>.</span><span>Include</span><span>.</span><span>NON_NULL</span><span>)</span>@JsonInclude(JsonInclude.Include.NON_NULL)
Enter fullscreen mode Exit fullscreen mode
Ignore unknown properties
Emarsys always returns all fields for a contact in the response. I want only the fields defined in the ContactDto to be mapped so that no exceptions are thrown. The annotation @JsonIgnoreProperties
can be used for this:
<span>@JsonIgnoreProperties</span><span>(</span><span>ignoreUnknown</span> <span>=</span> <span>true</span><span>)</span><span>@JsonIgnoreProperties</span><span>(</span><span>ignoreUnknown</span> <span>=</span> <span>true</span><span>)</span>@JsonIgnoreProperties(ignoreUnknown = true)
Enter fullscreen mode Exit fullscreen mode
MappingValueSerializer and MappingValueDeserializer
In order for the MappingTable, which is defined at the field, to be read, the interface ContextualSerializer
must be implemented for the MappingValueSerializer
and the interface ContextualDeserializer
for the MappingValueDeserializer
.
With createContextual()
, access to the property is possible and via BeanProperty
the annotation can be fetched and the MappingTable can be read out.
MappingValueSerializer
In the MappingValueSerializer
, for example, for salutation
“FEMALE” is mapped to “2” and marketingInformation
true to “1”, which is why the FieldValueID is written with jsonGenerator.writeString()
.
<span>package</span> <span>com.article.jackson.serializer</span><span>;</span><span>import</span> <span>java.io.IOException</span><span>;</span><span>import</span> <span>java.util.Map</span><span>;</span><span>import</span> <span>com.article.jackson.annotation.MappingTableMapReader</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.core.JsonGenerator</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.BeanProperty</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.JsonSerializer</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.SerializerProvider</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.ser.ContextualSerializer</span><span>;</span><span>public</span> <span>class</span> <span>MappingValueSerializer</span> <span>extends</span> <span>JsonSerializer</span><span><</span><span>MappingValue</span><span><?>></span> <span>implements</span> <span>ContextualSerializer</span> <span>{</span><span>private</span> <span>final</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>;</span><span>public</span> <span>MappingValueSerializer</span><span>()</span> <span>{</span><span>this</span><span>(</span><span>null</span><span>);</span><span>}</span><span>public</span> <span>MappingValueSerializer</span><span>(</span><span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>)</span> <span>{</span><span>this</span><span>.</span><span>map</span> <span>=</span> <span>map</span><span>;</span><span>}</span><span>@Override</span><span>public</span> <span>void</span> <span>serialize</span><span>(</span><span>MappingValue</span><span><?></span> <span>field</span><span>,</span> <span>JsonGenerator</span> <span>jsonGenerator</span><span>,</span> <span>SerializerProvider</span> <span>serializerProvider</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span><span>String</span> <span>fieldValueId</span> <span>=</span> <span>this</span><span>.</span><span>map</span><span>.</span><span>entrySet</span><span>()</span><span>.</span><span>stream</span><span>()</span><span>.</span><span>filter</span><span>(</span><span>e</span> <span>-></span> <span>e</span><span>.</span><span>getValue</span><span>().</span><span>equals</span><span>(</span><span>field</span><span>.</span><span>getValue</span><span>()))</span><span>.</span><span>map</span><span>(</span><span>Map</span><span>.</span><span>Entry</span><span>::</span><span>getKey</span><span>)</span><span>.</span><span>findFirst</span><span>()</span><span>.</span><span>orElse</span><span>(</span><span>null</span><span>);</span><span>jsonGenerator</span><span>.</span><span>writeString</span><span>(</span><span>fieldValueId</span><span>);</span><span>}</span><span>@Override</span><span>public</span> <span>JsonSerializer</span><span><?></span> <span>createContextual</span><span>(</span><span>SerializerProvider</span> <span>prov</span><span>,</span> <span>BeanProperty</span> <span>property</span><span>)</span> <span>{</span><span>return</span> <span>new</span> <span>MappingValueSerializer</span><span>(</span><span>new</span> <span>MappingTableMapReader</span><span>(</span><span>property</span><span>).</span><span>getMap</span><span>()</span><span>);</span><span>}</span><span>}</span><span>package</span> <span>com.article.jackson.serializer</span><span>;</span> <span>import</span> <span>java.io.IOException</span><span>;</span> <span>import</span> <span>java.util.Map</span><span>;</span> <span>import</span> <span>com.article.jackson.annotation.MappingTableMapReader</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.core.JsonGenerator</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.BeanProperty</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.JsonSerializer</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.SerializerProvider</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.ser.ContextualSerializer</span><span>;</span> <span>public</span> <span>class</span> <span>MappingValueSerializer</span> <span>extends</span> <span>JsonSerializer</span><span><</span><span>MappingValue</span><span><?>></span> <span>implements</span> <span>ContextualSerializer</span> <span>{</span> <span>private</span> <span>final</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>;</span> <span>public</span> <span>MappingValueSerializer</span><span>()</span> <span>{</span> <span>this</span><span>(</span><span>null</span><span>);</span> <span>}</span> <span>public</span> <span>MappingValueSerializer</span><span>(</span><span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>map</span> <span>=</span> <span>map</span><span>;</span> <span>}</span> <span>@Override</span> <span>public</span> <span>void</span> <span>serialize</span><span>(</span><span>MappingValue</span><span><?></span> <span>field</span><span>,</span> <span>JsonGenerator</span> <span>jsonGenerator</span><span>,</span> <span>SerializerProvider</span> <span>serializerProvider</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span> <span>String</span> <span>fieldValueId</span> <span>=</span> <span>this</span><span>.</span><span>map</span><span>.</span><span>entrySet</span><span>()</span> <span>.</span><span>stream</span><span>()</span> <span>.</span><span>filter</span><span>(</span><span>e</span> <span>-></span> <span>e</span><span>.</span><span>getValue</span><span>().</span><span>equals</span><span>(</span><span>field</span><span>.</span><span>getValue</span><span>()))</span> <span>.</span><span>map</span><span>(</span><span>Map</span><span>.</span><span>Entry</span><span>::</span><span>getKey</span><span>)</span> <span>.</span><span>findFirst</span><span>()</span> <span>.</span><span>orElse</span><span>(</span><span>null</span><span>);</span> <span>jsonGenerator</span><span>.</span><span>writeString</span><span>(</span><span>fieldValueId</span><span>);</span> <span>}</span> <span>@Override</span> <span>public</span> <span>JsonSerializer</span><span><?></span> <span>createContextual</span><span>(</span><span>SerializerProvider</span> <span>prov</span><span>,</span> <span>BeanProperty</span> <span>property</span><span>)</span> <span>{</span> <span>return</span> <span>new</span> <span>MappingValueSerializer</span><span>(</span> <span>new</span> <span>MappingTableMapReader</span><span>(</span><span>property</span><span>).</span><span>getMap</span><span>()</span> <span>);</span> <span>}</span> <span>}</span>package com.article.jackson.serializer; import java.io.IOException; import java.util.Map; import com.article.jackson.annotation.MappingTableMapReader; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.ContextualSerializer; public class MappingValueSerializer extends JsonSerializer<MappingValue<?>> implements ContextualSerializer { private final Map<String, Object> map; public MappingValueSerializer() { this(null); } public MappingValueSerializer(Map<String, Object> map) { this.map = map; } @Override public void serialize(MappingValue<?> field, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { String fieldValueId = this.map.entrySet() .stream() .filter(e -> e.getValue().equals(field.getValue())) .map(Map.Entry::getKey) .findFirst() .orElse(null); jsonGenerator.writeString(fieldValueId); } @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) { return new MappingValueSerializer( new MappingTableMapReader(property).getMap() ); } }
Enter fullscreen mode Exit fullscreen mode
MappingValueDeserializer
In the MappingValueDeserializer
the mapping takes place backwards. Here the FieldValueID for salutation
and marketingInformation
must be mapped accordingly. For salutation
“2” to “FEMALE” (String) and for marketingInformation
“1” to true (Boolean).
<span>package</span> <span>com.article.jackson.serializer</span><span>;</span><span>import</span> <span>java.io.IOException</span><span>;</span><span>import</span> <span>java.util.Map</span><span>;</span><span>import</span> <span>com.article.jackson.annotation.MappingTableMapReader</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.core.JsonParser</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.BeanProperty</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.DeserializationContext</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.JsonDeserializer</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.deser.ContextualDeserializer</span><span>;</span><span>public</span> <span>class</span> <span>MappingValueDeserializer</span> <span>extends</span> <span>JsonDeserializer</span><span><</span><span>MappingValue</span><span><?>></span> <span>implements</span> <span>ContextualDeserializer</span> <span>{</span><span>private</span> <span>final</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>;</span><span>public</span> <span>MappingValueDeserializer</span><span>()</span> <span>{</span><span>this</span><span>(</span><span>null</span><span>);</span><span>}</span><span>public</span> <span>MappingValueDeserializer</span><span>(</span><span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>)</span> <span>{</span><span>this</span><span>.</span><span>map</span> <span>=</span> <span>map</span><span>;</span><span>}</span><span>@Override</span><span>public</span> <span>MappingValue</span><span><?></span> <span>deserialize</span><span>(</span><span>JsonParser</span> <span>jsonParser</span><span>,</span> <span>DeserializationContext</span> <span>deserializationContext</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span><span>String</span> <span>fieldValue</span> <span>=</span> <span>jsonParser</span><span>.</span><span>getText</span><span>();</span><span>return</span> <span>new</span> <span>MappingValue</span><span><>(</span><span>this</span><span>.</span><span>map</span><span>.</span><span>entrySet</span><span>()</span><span>.</span><span>stream</span><span>()</span><span>.</span><span>filter</span><span>(</span><span>e</span> <span>-></span> <span>e</span><span>.</span><span>getKey</span><span>().</span><span>equals</span><span>(</span><span>fieldValue</span><span>))</span><span>.</span><span>map</span><span>(</span><span>Map</span><span>.</span><span>Entry</span><span>::</span><span>getValue</span><span>)</span><span>.</span><span>findFirst</span><span>()</span><span>.</span><span>orElse</span><span>(</span><span>null</span><span>));</span><span>}</span><span>@Override</span><span>public</span> <span>JsonDeserializer</span><span><?></span> <span>createContextual</span><span>(</span><span>DeserializationContext</span> <span>ctxt</span><span>,</span> <span>BeanProperty</span> <span>property</span><span>)</span> <span>{</span><span>return</span> <span>new</span> <span>MappingValueDeserializer</span><span>(</span><span>new</span> <span>MappingTableMapReader</span><span>(</span><span>property</span><span>).</span><span>getMap</span><span>()</span><span>);</span><span>}</span><span>}</span><span>package</span> <span>com.article.jackson.serializer</span><span>;</span> <span>import</span> <span>java.io.IOException</span><span>;</span> <span>import</span> <span>java.util.Map</span><span>;</span> <span>import</span> <span>com.article.jackson.annotation.MappingTableMapReader</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.core.JsonParser</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.BeanProperty</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.DeserializationContext</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.JsonDeserializer</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.deser.ContextualDeserializer</span><span>;</span> <span>public</span> <span>class</span> <span>MappingValueDeserializer</span> <span>extends</span> <span>JsonDeserializer</span><span><</span><span>MappingValue</span><span><?>></span> <span>implements</span> <span>ContextualDeserializer</span> <span>{</span> <span>private</span> <span>final</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>;</span> <span>public</span> <span>MappingValueDeserializer</span><span>()</span> <span>{</span> <span>this</span><span>(</span><span>null</span><span>);</span> <span>}</span> <span>public</span> <span>MappingValueDeserializer</span><span>(</span><span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>map</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>map</span> <span>=</span> <span>map</span><span>;</span> <span>}</span> <span>@Override</span> <span>public</span> <span>MappingValue</span><span><?></span> <span>deserialize</span><span>(</span><span>JsonParser</span> <span>jsonParser</span><span>,</span> <span>DeserializationContext</span> <span>deserializationContext</span><span>)</span> <span>throws</span> <span>IOException</span> <span>{</span> <span>String</span> <span>fieldValue</span> <span>=</span> <span>jsonParser</span><span>.</span><span>getText</span><span>();</span> <span>return</span> <span>new</span> <span>MappingValue</span><span><>(</span><span>this</span><span>.</span><span>map</span><span>.</span><span>entrySet</span><span>()</span> <span>.</span><span>stream</span><span>()</span> <span>.</span><span>filter</span><span>(</span><span>e</span> <span>-></span> <span>e</span><span>.</span><span>getKey</span><span>().</span><span>equals</span><span>(</span><span>fieldValue</span><span>))</span> <span>.</span><span>map</span><span>(</span><span>Map</span><span>.</span><span>Entry</span><span>::</span><span>getValue</span><span>)</span> <span>.</span><span>findFirst</span><span>()</span> <span>.</span><span>orElse</span><span>(</span><span>null</span><span>));</span> <span>}</span> <span>@Override</span> <span>public</span> <span>JsonDeserializer</span><span><?></span> <span>createContextual</span><span>(</span><span>DeserializationContext</span> <span>ctxt</span><span>,</span> <span>BeanProperty</span> <span>property</span><span>)</span> <span>{</span> <span>return</span> <span>new</span> <span>MappingValueDeserializer</span><span>(</span> <span>new</span> <span>MappingTableMapReader</span><span>(</span><span>property</span><span>).</span><span>getMap</span><span>()</span> <span>);</span> <span>}</span> <span>}</span>package com.article.jackson.serializer; import java.io.IOException; import java.util.Map; import com.article.jackson.annotation.MappingTableMapReader; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.deser.ContextualDeserializer; public class MappingValueDeserializer extends JsonDeserializer<MappingValue<?>> implements ContextualDeserializer { private final Map<String, Object> map; public MappingValueDeserializer() { this(null); } public MappingValueDeserializer(Map<String, Object> map) { this.map = map; } @Override public MappingValue<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { String fieldValue = jsonParser.getText(); return new MappingValue<>(this.map.entrySet() .stream() .filter(e -> e.getKey().equals(fieldValue)) .map(Map.Entry::getValue) .findFirst() .orElse(null)); } @Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) { return new MappingValueDeserializer( new MappingTableMapReader(property).getMap() ); } }
Enter fullscreen mode Exit fullscreen mode
MappingTableMapReader
In the MappingTableMapReader
class, the enum constant is retrieved from the annotation and is available in the JsonSerializer and JsonDeserializer.
<span>ackage</span> <span>com</span><span>.</span><span>article</span><span>.</span><span>jackson</span><span>.</span><span>annotation</span><span>;</span><span>import</span> <span>java.util.Map</span><span>;</span><span>import</span> <span>com.article.jackson.exception.MappingTableRuntimeException</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.BeanProperty</span><span>;</span><span>public</span> <span>class</span> <span>MappingTableMapReader</span> <span>{</span><span>private</span> <span>final</span> <span>BeanProperty</span> <span>property</span><span>;</span><span>public</span> <span>MappingTableMapReader</span><span>(</span><span>BeanProperty</span> <span>property</span><span>)</span> <span>{</span><span>this</span><span>.</span><span>property</span> <span>=</span> <span>property</span><span>;</span><span>}</span><span>public</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>getMap</span><span>()</span> <span>{</span><span>MappingTable</span> <span>annotation</span> <span>=</span> <span>property</span><span>.</span><span>getAnnotation</span><span>(</span><span>MappingTable</span><span>.</span><span>class</span><span>);</span><span>if</span> <span>(</span><span>annotation</span> <span>==</span> <span>null</span><span>)</span> <span>{</span><span>throw</span> <span>new</span> <span>MappingTableRuntimeException</span><span>(</span><span>String</span><span>.</span><span>format</span><span>(</span><span>"Annotation @MappingTable not set at property %s"</span><span>,</span><span>this</span><span>.</span><span>property</span><span>.</span><span>getMember</span><span>().</span><span>getFullName</span><span>()</span><span>)</span><span>);</span><span>}</span><span>return</span> <span>annotation</span><span>.</span><span>map</span><span>().</span><span>getMap</span><span>();</span><span>}</span><span>}</span><span>ackage</span> <span>com</span><span>.</span><span>article</span><span>.</span><span>jackson</span><span>.</span><span>annotation</span><span>;</span> <span>import</span> <span>java.util.Map</span><span>;</span> <span>import</span> <span>com.article.jackson.exception.MappingTableRuntimeException</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.BeanProperty</span><span>;</span> <span>public</span> <span>class</span> <span>MappingTableMapReader</span> <span>{</span> <span>private</span> <span>final</span> <span>BeanProperty</span> <span>property</span><span>;</span> <span>public</span> <span>MappingTableMapReader</span><span>(</span><span>BeanProperty</span> <span>property</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>property</span> <span>=</span> <span>property</span><span>;</span> <span>}</span> <span>public</span> <span>Map</span><span><</span><span>String</span><span>,</span> <span>Object</span><span>></span> <span>getMap</span><span>()</span> <span>{</span> <span>MappingTable</span> <span>annotation</span> <span>=</span> <span>property</span><span>.</span><span>getAnnotation</span><span>(</span><span>MappingTable</span><span>.</span><span>class</span><span>);</span> <span>if</span> <span>(</span><span>annotation</span> <span>==</span> <span>null</span><span>)</span> <span>{</span> <span>throw</span> <span>new</span> <span>MappingTableRuntimeException</span><span>(</span> <span>String</span><span>.</span><span>format</span><span>(</span> <span>"Annotation @MappingTable not set at property %s"</span><span>,</span> <span>this</span><span>.</span><span>property</span><span>.</span><span>getMember</span><span>().</span><span>getFullName</span><span>()</span> <span>)</span> <span>);</span> <span>}</span> <span>return</span> <span>annotation</span><span>.</span><span>map</span><span>().</span><span>getMap</span><span>();</span> <span>}</span> <span>}</span>ackage com.article.jackson.annotation; import java.util.Map; import com.article.jackson.exception.MappingTableRuntimeException; import com.fasterxml.jackson.databind.BeanProperty; public class MappingTableMapReader { private final BeanProperty property; public MappingTableMapReader(BeanProperty property) { this.property = property; } public Map<String, Object> getMap() { MappingTable annotation = property.getAnnotation(MappingTable.class); if (annotation == null) { throw new MappingTableRuntimeException( String.format( "Annotation @MappingTable not set at property %s", this.property.getMember().getFullName() ) ); } return annotation.map().getMap(); } }
Enter fullscreen mode Exit fullscreen mode
Functional Test
To check the implementation, we still need a test. To compare the JSON, I use assertThatJson()
from the package json-unit-assertj.
<span>package</span> <span>com.article.jackson.serializer</span><span>;</span><span>import</span> <span>java.io.IOException</span><span>;</span><span>import</span> <span>java.time.LocalDate</span><span>;</span><span>import</span> <span>com.article.jackson.dto.ContactDto</span><span>;</span><span>import</span> <span>com.article.jackson.exception.MappingTableRuntimeException</span><span>;</span><span>import</span> <span>com.article.jackson.fixtures.ContactDtoAnnotationNotSet</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.core.JsonProcessingException</span><span>;</span><span>import</span> <span>com.fasterxml.jackson.databind.ObjectMapper</span><span>;</span><span>import</span> <span>net.javacrumbs.jsonunit.core.Option</span><span>;</span><span>import</span> <span>org.junit.jupiter.api.Test</span><span>;</span><span>import</span> <span>static</span> <span>net</span><span>.</span><span>javacrumbs</span><span>.</span><span>jsonunit</span><span>.</span><span>assertj</span><span>.</span><span>JsonAssertions</span><span>.</span><span>assertThatJson</span><span>;</span><span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertEquals</span><span>;</span><span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertTrue</span><span>;</span><span>class</span> <span>MappingTableSerializerDeserializerTest</span> <span>{</span><span>String</span> <span>emarsysPayload</span> <span>=</span> <span>""" { "1": "Jane", "2": "Doe", "3": "jane.doe@example.com", "4": "1989-11-09", "46": "2", "100674": "1" } """</span><span>;</span><span>@Test</span><span>void</span> <span>serialize</span><span>()</span> <span>throws</span> <span>IOException</span> <span>{</span><span>ContactDto</span> <span>contact</span> <span>=</span> <span>new</span> <span>ContactDto</span><span>();</span><span>contact</span><span>.</span><span>setSalutation</span><span>(</span><span>"FEMALE"</span><span>);</span><span>contact</span><span>.</span><span>setFirstname</span><span>(</span><span>"Jane"</span><span>);</span><span>contact</span><span>.</span><span>setLastname</span><span>(</span><span>"Doe"</span><span>);</span><span>contact</span><span>.</span><span>setEmail</span><span>(</span><span>"jane.doe@example.com"</span><span>);</span><span>contact</span><span>.</span><span>setBirthday</span><span>(</span><span>LocalDate</span><span>.</span><span>of</span><span>(</span><span>1989</span><span>,</span> <span>11</span><span>,</span> <span>9</span><span>));</span><span>contact</span><span>.</span><span>setMarketingInformation</span><span>(</span><span>true</span><span>);</span><span>String</span> <span>json</span> <span>=</span> <span>new</span> <span>ObjectMapper</span><span>().</span><span>writeValueAsString</span><span>(</span><span>contact</span><span>);</span><span>assertThatJson</span><span>(</span><span>this</span><span>.</span><span>emarsysPayload</span><span>)</span><span>.</span><span>when</span><span>(</span><span>Option</span><span>.</span><span>IGNORING_ARRAY_ORDER</span><span>)</span><span>.</span><span>isEqualTo</span><span>(</span><span>json</span><span>);</span><span>}</span><span>@Test</span><span>void</span> <span>deserialize</span><span>()</span> <span>throws</span> <span>IOException</span> <span>{</span><span>ContactDto</span> <span>contact</span> <span>=</span> <span>new</span> <span>ObjectMapper</span><span>().</span><span>readValue</span><span>(</span><span>this</span><span>.</span><span>emarsysPayload</span><span>,</span> <span>ContactDto</span><span>.</span><span>class</span><span>);</span><span>assertEquals</span><span>(</span><span>"FEMALE"</span><span>,</span> <span>contact</span><span>.</span><span>getSalutation</span><span>());</span><span>assertEquals</span><span>(</span><span>"Jane"</span><span>,</span> <span>contact</span><span>.</span><span>getFirstname</span><span>());</span><span>assertEquals</span><span>(</span><span>"Doe"</span><span>,</span> <span>contact</span><span>.</span><span>getLastname</span><span>());</span><span>assertEquals</span><span>(</span><span>"jane.doe@example.com"</span><span>,</span> <span>contact</span><span>.</span><span>getEmail</span><span>());</span><span>assertEquals</span><span>(</span><span>LocalDate</span><span>.</span><span>of</span><span>(</span><span>1989</span><span>,</span> <span>11</span><span>,</span> <span>9</span><span>),</span> <span>contact</span><span>.</span><span>getBirthday</span><span>());</span><span>assertTrue</span><span>(</span><span>contact</span><span>.</span><span>getMarketingInformation</span><span>());</span><span>assertTrue</span><span>(</span><span>contact</span><span>.</span><span>isMarketingInformation</span><span>());</span><span>}</span><span>}</span><span>package</span> <span>com.article.jackson.serializer</span><span>;</span> <span>import</span> <span>java.io.IOException</span><span>;</span> <span>import</span> <span>java.time.LocalDate</span><span>;</span> <span>import</span> <span>com.article.jackson.dto.ContactDto</span><span>;</span> <span>import</span> <span>com.article.jackson.exception.MappingTableRuntimeException</span><span>;</span> <span>import</span> <span>com.article.jackson.fixtures.ContactDtoAnnotationNotSet</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.core.JsonProcessingException</span><span>;</span> <span>import</span> <span>com.fasterxml.jackson.databind.ObjectMapper</span><span>;</span> <span>import</span> <span>net.javacrumbs.jsonunit.core.Option</span><span>;</span> <span>import</span> <span>org.junit.jupiter.api.Test</span><span>;</span> <span>import</span> <span>static</span> <span>net</span><span>.</span><span>javacrumbs</span><span>.</span><span>jsonunit</span><span>.</span><span>assertj</span><span>.</span><span>JsonAssertions</span><span>.</span><span>assertThatJson</span><span>;</span> <span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertEquals</span><span>;</span> <span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertTrue</span><span>;</span> <span>class</span> <span>MappingTableSerializerDeserializerTest</span> <span>{</span> <span>String</span> <span>emarsysPayload</span> <span>=</span> <span>""" { "1": "Jane", "2": "Doe", "3": "jane.doe@example.com", "4": "1989-11-09", "46": "2", "100674": "1" } """</span><span>;</span> <span>@Test</span> <span>void</span> <span>serialize</span><span>()</span> <span>throws</span> <span>IOException</span> <span>{</span> <span>ContactDto</span> <span>contact</span> <span>=</span> <span>new</span> <span>ContactDto</span><span>();</span> <span>contact</span><span>.</span><span>setSalutation</span><span>(</span><span>"FEMALE"</span><span>);</span> <span>contact</span><span>.</span><span>setFirstname</span><span>(</span><span>"Jane"</span><span>);</span> <span>contact</span><span>.</span><span>setLastname</span><span>(</span><span>"Doe"</span><span>);</span> <span>contact</span><span>.</span><span>setEmail</span><span>(</span><span>"jane.doe@example.com"</span><span>);</span> <span>contact</span><span>.</span><span>setBirthday</span><span>(</span><span>LocalDate</span><span>.</span><span>of</span><span>(</span><span>1989</span><span>,</span> <span>11</span><span>,</span> <span>9</span><span>));</span> <span>contact</span><span>.</span><span>setMarketingInformation</span><span>(</span><span>true</span><span>);</span> <span>String</span> <span>json</span> <span>=</span> <span>new</span> <span>ObjectMapper</span><span>().</span><span>writeValueAsString</span><span>(</span><span>contact</span><span>);</span> <span>assertThatJson</span><span>(</span><span>this</span><span>.</span><span>emarsysPayload</span><span>)</span> <span>.</span><span>when</span><span>(</span><span>Option</span><span>.</span><span>IGNORING_ARRAY_ORDER</span><span>)</span> <span>.</span><span>isEqualTo</span><span>(</span><span>json</span><span>);</span> <span>}</span> <span>@Test</span> <span>void</span> <span>deserialize</span><span>()</span> <span>throws</span> <span>IOException</span> <span>{</span> <span>ContactDto</span> <span>contact</span> <span>=</span> <span>new</span> <span>ObjectMapper</span><span>().</span><span>readValue</span><span>(</span><span>this</span><span>.</span><span>emarsysPayload</span><span>,</span> <span>ContactDto</span><span>.</span><span>class</span><span>);</span> <span>assertEquals</span><span>(</span><span>"FEMALE"</span><span>,</span> <span>contact</span><span>.</span><span>getSalutation</span><span>());</span> <span>assertEquals</span><span>(</span><span>"Jane"</span><span>,</span> <span>contact</span><span>.</span><span>getFirstname</span><span>());</span> <span>assertEquals</span><span>(</span><span>"Doe"</span><span>,</span> <span>contact</span><span>.</span><span>getLastname</span><span>());</span> <span>assertEquals</span><span>(</span><span>"jane.doe@example.com"</span><span>,</span> <span>contact</span><span>.</span><span>getEmail</span><span>());</span> <span>assertEquals</span><span>(</span><span>LocalDate</span><span>.</span><span>of</span><span>(</span><span>1989</span><span>,</span> <span>11</span><span>,</span> <span>9</span><span>),</span> <span>contact</span><span>.</span><span>getBirthday</span><span>());</span> <span>assertTrue</span><span>(</span><span>contact</span><span>.</span><span>getMarketingInformation</span><span>());</span> <span>assertTrue</span><span>(</span><span>contact</span><span>.</span><span>isMarketingInformation</span><span>());</span> <span>}</span> <span>}</span>package com.article.jackson.serializer; import java.io.IOException; import java.time.LocalDate; import com.article.jackson.dto.ContactDto; import com.article.jackson.exception.MappingTableRuntimeException; import com.article.jackson.fixtures.ContactDtoAnnotationNotSet; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import net.javacrumbs.jsonunit.core.Option; import org.junit.jupiter.api.Test; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class MappingTableSerializerDeserializerTest { String emarsysPayload = """ { "1": "Jane", "2": "Doe", "3": "jane.doe@example.com", "4": "1989-11-09", "46": "2", "100674": "1" } """; @Test void serialize() throws IOException { ContactDto contact = new ContactDto(); contact.setSalutation("FEMALE"); contact.setFirstname("Jane"); contact.setLastname("Doe"); contact.setEmail("jane.doe@example.com"); contact.setBirthday(LocalDate.of(1989, 11, 9)); contact.setMarketingInformation(true); String json = new ObjectMapper().writeValueAsString(contact); assertThatJson(this.emarsysPayload) .when(Option.IGNORING_ARRAY_ORDER) .isEqualTo(json); } @Test void deserialize() throws IOException { ContactDto contact = new ObjectMapper().readValue(this.emarsysPayload, ContactDto.class); assertEquals("FEMALE", contact.getSalutation()); assertEquals("Jane", contact.getFirstname()); assertEquals("Doe", contact.getLastname()); assertEquals("jane.doe@example.com", contact.getEmail()); assertEquals(LocalDate.of(1989, 11, 9), contact.getBirthday()); assertTrue(contact.getMarketingInformation()); assertTrue(contact.isMarketingInformation()); } }
Enter fullscreen mode Exit fullscreen mode
Full example on GitHub
https://github.com/alaugks/article-jackson-serializer
Updates
- Change GitHub Repository URL (Sep 6th 2024)
- Replace JSON with HashMaps (Dec 19th 2024)
Mapping FieldValueIDs for the payload of the Emarsys API (3 Part Series)
1 Create a custom Symfony Normalizer and Denormalizer for mapping values
2 Create a custom JMS Serializer handler for mapping values
3 Create a custom Jackson JsonSerializer und JsonDeserializer for mapping values
原文链接:Create a custom Jackson JsonSerializer und JsonDeserializer for mapping values
暂无评论内容