lombok中aBcd型字段序列化时踩坑
问题描述
在进行模型的定义过程中,发现导出yaml接口文档时,出现了两个近似的字段,如图
造成客户端使用该模型序列化时会出现问题
Jackson序列化选择题
选项
分别将下列示例Bean中的cFlag赋值true,userName赋值abc,根据以下1-5的Bean选择
A:{“user-name”:“abc”,“cflag”:true}
B:{“user-name”:“abc”,“c-flag”:true}
C:{“user-name”:“abc”,“cflag”:true,“c-flag”:true}
示例1
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman1 {
private Boolean cFlag;
private String useName;
}
示例2
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
public class Woman2 {
@JsonProperty("c-flag")
private Boolean cFlag;
@JsonProperty("user-name")
private String useName;
}
示例3
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman3 {
@JsonProperty("c-flag")
private Boolean cFlag;
@JsonProperty("user-name")
private String useName;
}
示例4
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman4 {
@JsonProperty("c-flag")
private Boolean cFlag;
@JsonProperty("user-name")
private String useName;
public Boolean getcFlag() {
return cFlag;
}
public void setcFlag(Boolean cFlag) {
this.cFlag = cFlag;
}
public String getUseName() {
return useName;
}
public void setUseName(String useName) {
this.useName = useName;
}
}
示例5
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman5 {
private Boolean cFlag;
private String useName;
public Boolean getcFlag() {
return cFlag;
}
public void setcFlag(Boolean cFlag) {
this.cFlag = cFlag;
}
public String getUseName() {
return useName;
}
public void setUseName(String useName) {
this.useName = useName;
}
}
示例6
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman6 {
@JsonProperty("cflag")
private Boolean cFlag;
private String useName;
public Boolean getcFlag() {
return cFlag;
}
}
分析
打印的JSON序列化字符串结果分别为:
{“user-name”:“abc”,“cflag”:true}
{“cflag”:true,“c-flag”:true,“user-name”:“abc”}
{“cflag”:true,“c-flag”:true,“user-name”:“abc”}
{“c-flag”:true,“user-name”:“abc”}
{“c-flag”:true,“use-name”:“abc”}
{“use-name”:“abc”,“cflag”:true}
发现在二三两个示例中序列化出的结果多出了cflag这个字段,该字段都是get方法生成的,由于@Getter生成的方法分别为getCFlag所以解析时对应的字段为cflag
//@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman1 {
private Boolean cFlag;
private String userName;
}
打印返回{},即@JsonNaming需求与@Getter方法配合使用,才能正常序列化
//@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman1 {
@JsonProperty("cflag")
private Boolean cFlag;
private String userName;
}
打印{“cflag”:true},而JsonProperty则并非一定需要get方法
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman1 {
private Boolean cFlag;
private String userName;
}
打印{“user-name”:“abc”,“cflag”:true},为何使用了KebabCaseStrategy,最后序列化并非c-flag,原因在于Getter方法,生成了getCFlag,这个对应字段为cflag
换个角度,KebabCaseStrategy是对于getXXX形成的字段名而后进行的转换操作。
总结
1、直接避免定义aBcd型的字段
2、不使用lombok,使用IDE自动生成get方法,注意此时
2.1 使用@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)注解时,aBcd字段序列化的字符串为a-bcd
3、使用lombok的get,针对aBcd型的字段,单独定义get方法
3.1 使用@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)注解时,aBcd字段序列化的字符串为a-bcd
4、使用lombok的get,保证JSON序列化后的字段为abcd型,如字段cFlag定义为@JsonProperty(“cflag”)
5、不要单独定义isXXX方法
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Woman1 {
@JsonProperty("cflag")//必须
private Boolean cFlag;
private String useName;
}
最佳实践
不定义aBcd型的字段
针对aBcd型的字段,IDE生成getaBcd方法,并单独明确指定字段@JsonProperty
Jackson工具类
package com.zte.sunquan.lombok.json;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import org.json.simple.JSONObject;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class JacksonUtil {
private static ObjectMapper MAPPER = initMapper();
private static final String SLASH = "/";
public JacksonUtil() {
}
public static String toJson(Object input) {
try {
return MAPPER.writeValueAsString(input);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
public static String toJsonRemoveSlash(Object input) {
String ex = null;
try {
ex = MAPPER.writeValueAsString(input);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return ex != null && !ex.isEmpty() ? ex.replace("/", "") : null;
}
public static <T> T fromJson(String input, Class<T> valueType) {
try {
return MAPPER.readValue(input, valueType);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static <T> List<T> fromArrayJson(String input, Class<T> valueType) {
try {
return (List) MAPPER.readValue(input, MAPPER.getTypeFactory().constructCollectionType(List.class, valueType));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String mergeJsonObject(String basicJson, String updateJson) {
if (basicJson != null && !basicJson.isEmpty()) {
if (updateJson != null && !basicJson.isEmpty()) {
JSONObject obj1 = (JSONObject) fromJson(basicJson, JSONObject.class);
JSONObject obj2 = (JSONObject) fromJson(updateJson, JSONObject.class);
JSONObject merged = new JSONObject();
JSONObject[] objs = new JSONObject[]{obj1, obj2};
JSONObject[] var6 = objs;
int var7 = objs.length;
for (int var8 = 0; var8 < var7; ++var8) {
JSONObject obj = var6[var8];
Iterator var10 = obj.entrySet().iterator();
while (var10.hasNext()) {
Object jsonElement = var10.next();
Entry entry = (Entry) jsonElement;
merged.put(entry.getKey(), entry.getValue());
}
}
return toJson(merged);
} else {
return basicJson;
}
} else {
return updateJson;
}
}
private static ObjectMapper initMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, false);
mapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
mapper.configure(Feature.ALLOW_SINGLE_QUOTES, true);
mapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
mapper.configure(com.fasterxml.jackson.core.JsonGenerator.Feature.IGNORE_UNKNOWN, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
public static void setMapper(ObjectMapper mapper) {
MAPPER = mapper;
}
}