用lombok进行Bean序列化时bug

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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值