聊聊springboot專案中使用jackson的一些小技巧

linyb极客之路發表於2024-11-19

前言

在我們前後端聯調時,很經常以json作為資料的互動格式,今天我們就來聊聊在開發springboot專案中,使用jackson進行資料渲染一些小技巧

場景一:列舉-JSON互轉

在日常開發中我們為了避免過多的魔法值,使用列舉類來封裝一些靜態的狀態程式碼。
但是在將這些列舉的意思正確而全面的返回給前端卻並不是那麼順利,比如有個狀態列舉類

public enum StatusEnums {

    NORMAL(1,"正常"),

    LOCK(2,"鎖定"),

    DELETE(3,"刪除");

    private Integer code;

    private String desc;

    StatusEnums(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }


    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }



}

如果我們直接使用Jackson對列舉進行序列化,將只能簡單的輸出列舉的String名稱

new ObjectMapper().writeValueAsString(StatusEnums.NORMAL);

輸出結果為列舉名稱:

NORMAL

而我們希望將列舉轉為JSON物件,像下面這樣:

{"code":200,"desc":"正常"}

那如何達到以上效果

方法一:使用@JsonValue 註解

我們透過@JsonValue註解,來控制列舉序列化結果

    @JsonValue
    public Integer getCode() {
        return code;
    }

比如StatusEnums.NORMAL最後最終序列化的值:

1

同樣我們也可以透過@JsonValue註解進行反序列化,最終1反序列化的值為

NORMAL
方法二:透過自定義序列化/反序列器

a、 自定義序列化器以及反序列化器

public class StatusEnumsJsonSerializer extends JsonSerializer<StatusEnums> {

    @Override
    public void serialize(StatusEnums value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(value.getCode().toString());
    }
}
public class StatusEnumsJsonDerializer extends JsonDeserializer<StatusEnums> {


    @Override
    public StatusEnums deserialize(JsonParser p, DeserializationContext ctx) throws IOException, JsonProcessingException {
         if(p.getText() != null){
             return StatusEnums.getByCode(Integer.valueOf(p.getText()));
         }
        return null;
    }
}

b、在列舉類上加上自定義序列化、反序列化註解

 @JsonSerialize(using = StatusEnumsJsonSerializer.class)
 @JsonDeserialize(using =  StatusEnumsJsonDerializer.class)
    private StatusEnums status;

除了以上方法,還可以透過@JsonCreator來達到上述效果,因為demo沒實現,這邊就不演示了

場景二:規避前端JavaScript接收後端Long屬性值,精度丟失問題

示例演示

後端示例程式碼
  @GetMapping("get")
    @ResponseBody
    public User getUser(){
       return User.builder().name("張三")
                .password("123456")
                .status(StatusEnums.NORMAL)
                .id(987654321123456789L).build();
    }
前端示例程式碼
<button id="loadDataButton">Load Data</button>
<div id="dataDisplay"></div>

<script>
    $(document).ready(function() {
    $("#loadDataButton").click(function() {
        $.ajax({
            url: "/user/get",
            type: "GET",
            data: {},
            success: function(response) {
                $("#dataDisplay").html("id: " + response.id + "<br/>name: " + response.name + "<br/>password:  " + response.password + "<br/>status:  " + response.status);
            },
            error: function(xhr, status, error) {
                console.error("Error: " + error);
            }
        });
    });
});
</script>
瀏覽器訪問結果


可以發現精度丟失了

方法一:透過@JsonSerialize註解,將long序列化為字串

注: 直接透過jackson自帶的序列化器

示例

@JsonSerialize(using = ToStringSerializer.class)
    private Long id;

加上該註解後,透過瀏覽器檢視效果

可以發現精度沒有丟失了

場景三:資料脫敏

敏感資料脫敏展示,應該是挺通用需求,我們可以透過自定義序列化器實現這一效果

1、自定義脫敏序列化器
RequiredArgsConstructor
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private final DesensitizedUtil.DesensitizedType desensitizedType;

    public SensitiveJsonSerializer(){
        this.desensitizedType = DesensitizedUtil.DesensitizedType.CLEAR_TO_EMPTY;
    }



    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(DesensitizedUtil.desensitized(value,desensitizedType));
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        Sensitive sensitive = property.getAnnotation(Sensitive.class);
        if (sensitive != null){
            return new SensitiveJsonSerializer(sensitive.type());
        }

        return prov.findValueSerializer(property.getType(), property);
    }
}
2、自定義脫敏註解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {

    DesensitizedUtil.DesensitizedType type();
}
3、在需要脫敏的欄位上,加上脫敏註解
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {

    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

    @Sensitive(type = DesensitizedUtil.DesensitizedType.CHINESE_NAME)
    private String name;

    @Sensitive(type = DesensitizedUtil.DesensitizedType.PASSWORD)
    private String password;
    }

a、 未加脫敏註解時,透過瀏覽器訪問

發現沒有脫敏,現在我們針對名字和密碼進行脫敏

b、 名字和密碼加上脫敏註解

    @Sensitive(type = DesensitizedUtil.DesensitizedType.CHINESE_NAME)
    private String name;

    @Sensitive(type = DesensitizedUtil.DesensitizedType.PASSWORD)
    private String password;

觀察瀏覽器

總結

本文介紹列舉和json轉換、long精度問題、資料脫敏三種我們日常開發比較常用的場景,但不知道大家發現沒,這三種場景本質上都是透過json的序列化和反序列化實現,因此我們可以透過定製全域性json序列化、反序列化器來實現。核心程式碼如下

    @Bean
    @ConditionalOnMissingBean
    public Jackson2ObjectMapperBuilderCustomizer customJackson2ObjectMapperBuilderCustomizer(){
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder
                .serializerByType(Long.class,ToStringSerializer.instance)
                .serializerByType(StatusEnums.class,new StatusEnumsJsonSerializer())
                .deserializerByType(StatusEnums.class,new StatusEnumsJsonDerializer());

    }

demo連結

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-json-render

相關文章