对于我的一系列文章,我还想看看如何使用Jackson实现此要求。
第一篇文章中的第一段“ The requirements and history”介绍了Emarsys重写有效载荷值的要求。
所需的软件包
- 杰克逊
- com.fasterxml.jackson.core:Jackson-Databin
- com.fasterxml.jackson.datatype:jackson-datatype-jsr310
- junit
- org.junit.jupiter:junit-jupiter-api
- org.junit.jupiter:junit-jupiter-engine
- jsonunit
- net.javacrumbs.json-unit:json-unit-assertj
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.14.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
</dependency>
<dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.json-unit</groupId>
<artifactId>json-unit-assertj</artifactId>
<version>2.37.0</version>
<scope>test</scope>
</dependency>
自定义jsonserializer和jsondeserializer的最小结构
要求解映射emarsys值的要求,需要自定义的jsonserializer和jsondeserializer。我称这些 mappingValueserializer 和 mappingValueDeserializer 。
以下是自定义MappingValueSerializer
和MappingValueDeserializer
的最小结构:
@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;
}
}
在contactdto中,salutation
和marketingInformation
必须重写值。
字段/方向 | 序列化(t->字符串) | deperialize(string-> t) |
---|---|---|
称呼 | “女性” - >“ 2” | “ 2” - >“女性” |
营销信息 | true->“ 1” | “ 1” - > true |
对于序列化过程,它是fieldValueId(字符串)和避难所的过程salutation
的类型字符串和marketingInformation
的类型布尔。
因此,如果要进行映射,则需要一个jsonserialializer来编写salutation
和marketingInformation
的fieldValueId(string)和jsondeserialializer来设置stalutation
(string)的值(string)和marketingInformation
(boolean)。
自定义类型
但是,我只想拥有一个可以处理字符串,布尔值和将来其他类型的jsondeserialializer。为此,我创建了自己的类型MappingValue<>
。最重要的是,我可以使用此自定义仿制药运输所有类型。
package com.microservice.crm.serializer;
public class MappingValue<T> {
T value;
public MappingValue(T value) {
this.value = value;
}
public T getValue() {
return this.value;
}
}
联系人
首先,所有字段和注释的完整联系人中的所有联系人。我将解释以下各个注释。
package com.microservice.crm.fixtures;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonFormat;
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;
import com.microservice.crm.annotation.*;
import com.microservice.crm.serializer.MappingValue;
import com.microservice.crm.serializer.MappingValueDeserializer;
import com.microservice.crm.serializer.MappingValueSerializer;
import java.time.LocalDate;
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE
)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
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 = "{\"1\": \"MALE\", \"2\": \"FEMALE\", \"6\": \"DIVERS\"}")
@JsonSerialize(using = MappingValueSerializer.class)
@JsonDeserialize(using = MappingValueDeserializer.class)
private MappingValue<String> salutation;
@JsonProperty("100674")
@MappingTable(map = "{\"1\": true, \"2\": false}")
@JsonSerialize(using = MappingValueSerializer.class)
@JsonDeserialize(using = MappingValueDeserializer.class)
private MappingValue<Boolean> marketingInformation;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDate getBirthday() {
return birthday;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
public String getSalutation() {
return salutation.getValue();
}
public void setSalutation(String salutation) {
this.salutation = new MappingValue<>(salutation);
}
public Boolean getMarketingInformation() {
return marketingInformation.getValue();
}
public void setMarketingInformation(Boolean marketingInformation) {
this.marketingInformation = new MappingValue<>(marketingInformation);
}
}
在田野上阅读和写作
杰克逊的对象管理员默认情况下在突变器(setter)和登录器(getter,isser)上写入并读取。
对于salutation
和marketingInformation
的突变器和登录器,我想定义类型字符串或布尔值。
您可以使用注释指示杰克逊仅在字段上读写,因此我们可以在内部使用自定义类型MappingValue <>。因此,阅读和写作过程发生在字段上,我们可以为salutation
和marketingInformation
的突变器和访问者定义字符串和布尔。
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE
)
fieldids
使用@JsonProperty
可以非常容易地定义野战。
@JsonProperty("123")
定义Custom Jsonserializer和Jsondeserializer
可以用@JsonSerialize
和@JsonDeserialize
定义自定义的jsonserializer(MappingValueSerializer
)和jsondeserializer(MappingValueDeserializer
)。
@JsonSerialize(using = MappingValueSerializer.class)
@JsonDeserialize(using = MappingValueDeserializer.class)
跳过零值
null为值的字段不应序列化。这是因为发送的字段也已更新。注释@JsonInclude
可用于此。
@JsonInclude(JsonInclude.Include.NON_NULL)
忽略未知属性
Emarsys总是返回所有字段,以进行响应中的联系。我只希望映射contactdto中定义的字段,以便没有例外。注释@JsonIgnoreProperties
可用于此:
@JsonIgnoreProperties(ignoreUnknown = true)
自定义注释@MappingTable可用于示意图
必须在MappingValueserizer和MappingValueDeserializer中使用salutation
和marketingInformation
的fieldValueids映射。
为此,我创建了一个自定义注释@MappingTable
,它将在mappingvalueserializer和mappingValueDeserializer中读取。
package com.microservice.crm.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface MappingTable {
String map() default "{}";
}
@MappingTable
在注释中定义为蒙版的json(字符串)。
只有以下类型的注释:
- 原始类型
- 字符串
- 枚举
- class(class <?>,class <?扩展/super t>)
- 上述数组(原始,枚举,字符串或类的数组[])
- 另一个注释。
请参阅Stackoverflow上的此conversation。
MappingValueserializer和MappingValueDeserializer
要读取在字段中定义的示波器,必须为MappingValueSerializer
实现接口ContextualSerializer
和MappingValueDeserializer
的接口ContextualDeserializer
。
使用createContextual()
,可以访问该属性,并且通过BeanProperty
可以获取注释,并且可以读取示意图。
MappingTableDataReader将json转换为哈希图。
MappingValueserializer
在MappingValueSerializer
中,对于salutation
“女性”映射到“ 2”,而marketingInformation
ture为“ 1”,这就是为什么fieldValueid用jsonGenerator.writeString()
编写的原因。
package com.microservice.crm.serializer;
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;
import com.microservice.crm.annotation.MappingTableDataReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class MappingValueSerializer extends JsonSerializer<MappingValue<?>> implements ContextualSerializer {
private final HashMap<String, ?> data;
public MappingValueSerializer() {
this(null);
}
public MappingValueSerializer(HashMap<String, ?> data) {
this.data = data;
}
@Override
public void serialize(MappingValue<?> value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String fieldId = this.data.entrySet().stream()
.filter(e -> e.getValue().equals(value.getValue()))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
jsonGenerator.writeString(fieldId);
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
return new MappingValueSerializer(
new MappingTableDataReader().getMap(property)
);
}
}
MappingValueDeserializer
在MappingValueDeserializer
中,映射发生在向后进行。在这里,必须相应地映射salutation
和marketingInformation
的fieldValueid。对于salutation
“ 2”到“女性”(string)和marketingInformation
“ 1”到true(boolean)。
package com.microservice.crm.serializer;
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;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.microservice.crm.annotation.MappingTableDataReader;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class MappingValueDeserializer extends JsonDeserializer<MappingValue<?>> implements ContextualDeserializer {
private final String[] supportedTypes = {"String", "Boolean"};
private final HashMap<String, ?> data;
private final Type type;
public MappingValueDeserializer() {
this(null, null);
}
public MappingValueDeserializer(HashMap<String, ?> data, Type type) {
this.data = data;
this.type = type;
}
@Override
public MappingValue<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String value = jsonParser.getText();
String simpleName = ((SimpleType) this.type).getBindings().getTypeParameters().get(0).getRawClass().getSimpleName();
if (Arrays.stream(supportedTypes).noneMatch(simpleName::equalsIgnoreCase)) {
throw new IOException(String.format("Type \"%s\" is currently not supported", simpleName));
}
return new MappingValue<>(this.data.entrySet().stream()
.filter(e -> e.getKey().equals(value))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null));
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
return new MappingValueDeserializer(
new MappingTableDataReader().getMap(property),
property.getType()
);
}
}
测试
要检查实现,我们仍然需要测试。要比较JSON,我使用json-unit-assertj的assertThatJson()
。
package com.microservice.crm.serializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microservice.crm.fixtures.ContactDto;
import net.javacrumbs.jsonunit.core.Option;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.time.LocalDate;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public 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.trim())
.when(Option.IGNORING_ARRAY_ORDER)
.isEqualTo(json);
}
@Test
void deserialize() throws IOException {
ContactDto contact = new ObjectMapper().readValue(this.emarsysPayload.trim(), 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());
}
}
全部
映射图定义为注释@MappingTable
的蒙版JSON。这意味着我们不能在任何其他情况下使用数据。由于注释不支持hashmap,因此无法定义哈希图。例如,解决方案是使用枚举类。
我将在以后的文章中如何解决这一问题。