Jackson 序列化 自定义注解处理Null 值

@JsonSerialize(using = ToStringSerializer.class)和@JsonSerialize注解的使用方法 关于这个注解的方法,可以自己定义class,作为using的值来使用。/*** 订单创建时间*/这个的意义就是当转成json时,把时间转成秒的格式。@Override创建一个注解类,统一帮助我们实现对BigDecimal此类型字段数值的封装操作if (value!= null) {} else {创建完注解类后,具体什么字段需要保留两位有效数字,就在对应的字段上加上此注解最后返回的值就是保留两位有效数字的。 阅读详情
AI摘要

本文介绍了如何在SpringBoot项目中整合自定义注解,实现在Model类中自动识别字段类型并使用相应的null值序列化器,避免了逐字段添加注解的繁琐。通过自定义注解和Jackson配置,简化了null值序列化过程。

问题引入

jackson 有原生的注解去处理 null 值

@JsonSerialize(nullsUsing = StringNullSerializer.class)

示例代码

package com.catdou.formatter.model;

import com.catdou.formatter.mvc.jackson.serializer.StringNullSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.util.List;

/**
 * @author James
 */

@JsonSerialize(nullsUsing = StringNullSerializer.class)
@Data
public class User {
    private String name;

    private String password;

    private List<String> addressList;
}


序列化类

package com.catdou.formatter.mvc.jackson.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

/**
 * @author James
 */
public class StringNullSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString("");
    }
}
package com.catdou.formatter.serializer;

import com.catdou.formatter.model.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author James
 */
public class NullValSerializerTest {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Test
    public void testSerializer() throws JsonProcessingException {
        User user = new User();
        user.setName("chengdu");
        String json = objectMapper.writeValueAsString(user);
        Assertions.assertEquals("{\"name\":\"chengdu\",\"password\":null,\"addressList\":null}", json);
    }
}

运行上面这个测试用例,发现 null 值没有处理成功, 序列化的结果为

{"name":"chengdu","password":null,"addressList":null}

然后将类上的注解@JsonSerialize 调整到字段上,调整之后的类如下

package com.catdou.formatter.model;

import com.catdou.formatter.mvc.jackson.serializer.StringNullSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.util.List;

/**
 * @author James
 */
@Data
public class UserChange {
    @JsonSerialize(nullsUsing = StringNullSerializer.class)
    private String name;

    @JsonSerialize(nullsUsing = StringNullSerializer.class)
    private String password;

    private List<String> addressList;
}

测试用例

package com.catdou.formatter.serializer;

import com.catdou.formatter.model.User;
import com.catdou.formatter.model.UserChange;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author James
 */
public class NullValSerializerTest {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Test
    public void testSerializer() throws JsonProcessingException {
        User user = new User();
        user.setName("chengdu");
        String json = objectMapper.writeValueAsString(user);
        Assertions.assertEquals("{\"name\":\"chengdu\",\"password\":null,\"addressList\":null}", json);
    }

    @Test
    public void testFieldSerializer() throws JsonProcessingException {
        UserChange user = new UserChange();
        user.setName("chengdu");
        String json = objectMapper.writeValueAsString(user);
        Assertions.assertEquals("{\"name\":\"chengdu\",\"password\":\"\",\"addressList\":null}", json);
    }
}

将注解调整到字段上,解决了null字符串序列化的问题,但又会引入如下问题

  1. 模型类上每个字段都需要增加序列化注解
  2. 人工识别字段类型,使用正确得序列化类(不能将List序列化成"")

解决问题

查看 @JsonSerialize(nullsUsing = StringNullSerializer.class) nullsUsing 的实现逻辑

  1. 下载源码
    https://github.com/FasterXML/jackson-databind

  2. 全局检索 nullsUsing 关键字
    在这里插入图片描述
    真好,只有4个地方有这些关键字,在对应的地方加上断点

  3. 开始调试
    第2步检索到测试类中有对应的注解,入口就从测试类开始
    在这里插入图片描述
    idea 直接F9 跳到对应的读取注解代码段
    在这里插入图片描述
    进入属性构建类这个地方,主要看这一段,给属性类分配 _nullSerializer

		// How about custom null serializer?
        Object serDef = _annotationIntrospector.findNullSerializer(am);
        if (serDef != null) {
            bpw.assignNullSerializer(prov.serializerInstance(am, serDef));
        }

在这里插入图片描述
继续跟踪发现啥时候使用这个类
在这里插入图片描述

自定义注解解决问题

通过直接的源码跟踪,发现一个可行的途径,可以自定义个查找 findNullSerializer 值的方法,按照自定义的解析规则分配 _nullSerializer。自定义的注解应该可以有以下功能

  1. 只要在类上面配置一个注解,自动识别每个字段的类型,使用对应的 null 值序列化器,同时可以忽略不支持的类型
  2. 支持增加自己配置的序列化类
  3. 字段上的注解优先级大于类上面的

于是,注解类出来了, 如下

package com.catdou.formatter.annotations;

import com.fasterxml.jackson.annotation.JacksonAnnotation;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author James
 */
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface NullValueHandler {
    Class<? extends JsonSerializer> using() default JsonSerializer.None.class;

    /**
     * 用在类上面。忽略某些注解
     * @return
     */
    Class<?>[] ignore() default JsonSerializer.None.class;
}

读取注解规则部份如下

package com.catdou.formatter.mvc.jackson;

import com.catdou.formatter.annotations.NullValueHandler;
import com.catdou.formatter.mvc.jackson.serializer.IntergerNullSerializer;
import com.catdou.formatter.mvc.jackson.serializer.ListNullSerializer;
import com.catdou.formatter.mvc.jackson.serializer.StringNullSerializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.PropertyBuilder;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author James
 */
public class NullValueSerializerInterceptor extends JacksonAnnotationIntrospector {

    private static Map<Class<?>, Class<? extends JsonSerializer>> serializerMap = new HashMap<>();

    static {
        serializerMap.put(String.class, StringNullSerializer.class);
        serializerMap.put(Integer.class, IntergerNullSerializer.class);
        serializerMap.put(List.class, ListNullSerializer.class);
    }

    /**
     *  返回 null 就不不处理 null
     *  @see BeanPropertyWriter#assignNullSerializer(com.fasterxml.jackson.databind.JsonSerializer)
     *  @see PropertyBuilder#buildWriter
     * @param a Annotated
     * @return
     */
    @Override
    public Object findNullSerializer(Annotated a) {
        NullValueHandler nullValueHandler = a.getAnnotation(NullValueHandler.class);
        if (nullValueHandler == null) {
            if (a instanceof AnnotatedMethod) {
                AnnotatedMethod annotatedMethod = (AnnotatedMethod) a;
                nullValueHandler = annotatedMethod.getDeclaringClass().getAnnotation(NullValueHandler.class);
            }
        }
        if (nullValueHandler != null) {
            Class<? extends JsonSerializer> using = nullValueHandler.using();
            if (using != JsonSerializer.None.class) {
                return using;
            } else {
                Class<?> filedType = a.getRawType();
                Set<Class<?>> ignoredSet = Arrays.stream(nullValueHandler.ignore()).collect(Collectors.toSet());
                if (ignoredSet.contains(filedType)) {
                    return null;
                }
                return serializerMap.get(filedType);
            }
        }
        return null;
    }

}

如何整合到 SpringBoot 项目

SpringBoot 加载 jackson 是自动装载的,在 org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration 这个类,如下图所示
在这里插入图片描述
启动时候配置引入自定义的注解解析规则
配置类

package com.catdou.formatter.mvc.config;

import com.catdou.formatter.mvc.jackson.NullValueSerializerInterceptor;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

/**
 * @author James
 */
public class JacksonConfig {

    public JacksonConfig(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
        mappingJackson2HttpMessageConverter.getObjectMapper()
                .setAnnotationIntrospector(new NullValueSerializerInterceptor());
    }
}

启动类

package com.catdou.formatter;

import com.catdou.formatter.mvc.config.JacksonConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

@SpringBootApplication
@Import(value = JacksonConfig.class)
public class FormatterApplication {

    public static void main(String[] args) {
        SpringApplication.run(FormatterApplication.class, args);
    }

}

项目地址

整合到 SpringBoot 项目地址
https://gitee.com/3281328128/formatter

欢迎大家关注我的公众号

程序员知识分享号

Spring Blade 开发笔记 Spring Blade 开发笔记 前言 这是一个微服务框架 1.配置 1.1基本环境 1.idea 2.nacos 需要配置 3.redis 4.mysql 5.navicat 1.2 IDEA插件 1.Free Mybatis plugin:在mapper和XML之间跳转 2.Hiberbee theme:多彩UI 3.Lombok:使用注解减少代码量,如:@Data 4.Mybatis Lo... 阅读详情

相关推荐

【数据挖掘】遗传算法原理浅析:什么是遗传算法?遗传算法的工作原理是什么?

遗传算法(Genetic Algorithm,GA),是进化算法的框架和核心。 各种进化算法有什么异同? - 及时物语的回答 - 知乎 https://www.zhihu.com/question/39152390/answer/901070749

纸羊同学的博客 4271

在接口返回对象的属性上加 @JsonSerialize(nullsUsing = NullSerializer.class) 可以防止为null的属性变为-1

在接口返回对象的属性上加 @JsonSerialize(nullsUsing = NullSerializer.class) 可以防止为null的属性变为-1

weixin_54369939的博客 898

Vmware安装windowsServer2022版本

全网最详细的Vmware安装windowsServer2022版本

A乐神的博客 7005

java-数据格式化(结果为null,返回前端显示-1)

JsonProperty:使用在某个属性上,这个注解有两个作用,第一是修改返回JSON数据的时候key值为value指定值,第二个作用是配合属性access=JsonProperty.Access.WRITE_ONLY表示属性只可以进行序列化而不能进行反序列化,直观效果就是返回的数据没有该属性。@JsonIgnore:使用在某个属性上,这样在序列化和反序列化的时候都会忽略这个属性,最直接的效果就是返回的JSON属性是没有这个属性的,一般作用于密码这系列的属性。

my的足迹 3951

【STM32单片机】RS485通信—基于STM32G474芯片

做项目需要用485通信来控制Go电机参考资料:【【STM32F103C8T6系列】10. 实验例程——485(USART串口)通讯实验】 https://www.bilibili.com/video/BV19w411r7ka/?PDF: STM32G474 开发指南 正点原子 DMG474 开发板教程 第28章485只是物理层,代码逻辑还是UART串口通信,相当于是电路有RS485转换芯片,所以还是用UART串口通信方式。

m0_48808835的博客 3647

双有源桥变换器(DAB)TPS控制:从理论建模到Simulink/PSIM仿真实践

本文深入探讨了双有源桥变换器(DAB)的三重移相(TPS)控制技术,从理论建模到仿真实现。文章详细分析了TPS相比SPS和DPS的优势,阐述了其12种工况的划分逻辑与电流应力最优调制策略,并提供了在Simulink和PSIM平台搭建仿真模型的详细步骤与实战经验,帮助工程师掌握这一高效控制方法。

white的博客 584

Long型数据后端查询结果为null,返回前端显示-1,使用@JsonSerialize注解

@JsonSerialize(nullsUsing = NullSerializer.class)

stupidkid521的博客 2866

基于MATLAB的脑功能网络工具箱:BCT与FCLab

脑功能网络分析是神经科学领域的一个重要研究方向,它通过分析大脑中神经元之间的连接模式和信息传递方式,揭示脑网络的结构和功能。在这个领域,MATLAB是一个广泛使用的工具,提供了丰富的函数和工具箱来进行脑功能网络的分析和可视化。这些工具箱提供了丰富的函数和工具,使得脑功能网络的计算和分析变得更加简单和高效。在上述示例代码中,我们首先导入BCT工具箱,并构建一个邻接矩阵来表示脑网络的连接关系。在上述示例代码中,我们首先导入FCLab工具箱,并导入脑功能数据。函数对数据进行预处理,如去除噪声、降采样等操作。

web_bug407的博客 2608

Jackson 自定义注解实现null值自定义序列化

Jackson 自定义注解实现null值自定义序列化Jackson 自定义注解实现null值自定义序列化目前简单的方式原理部分实现结果演示总结 Jackson 自定义注解实现null值自定义序列化 spring项目中都使用的是Jackson为默认的序列化方式,但是不免有时不满足项目的需要,例如以下场景: 返回前端时部分字段不能为null,需要默认值 某类型的字段需要默认值 某字段需要特殊默认值 综上各类场景,总而言之就是要对为 null 值的字段进行一些默认值赋值处理,让返回的json中不存在未null

Tophua的博客 1505

使用Python从图像中提取表格

python 实现图片表格提取

qq_30895747的博客 2570

操作系统权限提升(十五)之绕过UAC提权-基于白名单DLL劫持绕过UAC提权

先将操作在本地执行,使用procmon进行监控,过滤DLL和 NAME NOT FOUND ,寻找可以替换的DLL文件,注意文件的权限是否可以在目录写入,可以看到在C盘的tools目录的文件中有一个srrstr.dll被调用,但是没有加载,我们生成恶意的DLL文件看看是否可以被劫持。在Windows中许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库文件,即dll文件,放置于系统中,个人理解类似于我们编程中引入的模块。将我们生成的DLL文件改名为我们要劫持的DLL文件名。

把自己活成一道光,因为你不知道,谁会借着你的光,走出了黑暗。请保持心中的善良,因为你不知道,谁会借着你的善良,走出了绝望。请保持你心中的信仰,因为你不知道,谁会借着你的信仰,走出了迷茫。请相信自己的力量,因为你不知道,谁会因为相信你,开始相信了自己.... 1873

springboot——自定义jackson序列化(包括array、list、set)

jackson提供了一些注解来定义序列化的规则,但是针对array、list、set没有相对应的注解,所以需要我们自己来实现 1.实体上 @JsonInclude(Include.NON_NULL) 将该标记放在属性上,如果该属性为NULL则不参与序列化 如果放在类上边,那对这个类的全部属性起作用 Include.Include.ALWAYS 默认 Include.NON_DEFA...

qq_42133100的博客 8081

声音源定位:利用YOLO与麦克风阵列定位声音来源YOLOv5、YOLOv8、YOLOv10

YOLO(You Only Look Once)是一个用于目标检测的深度学习模型。YOLO的最大特点是其通过一次前向传播即可完成多个目标的检测,速度和精度相对其他目标检测算法具有显著优势。YOLO模型的版本不断更新,从YOLOv1到YOLOv10,算法精度和速度都有了显著的提升。声音源定位是利用多个麦克风阵列收集的声音信号,通过算法计算声音源的位置。声音源定位的关键在于准确估计每个麦克风的声音到达时间,从而确定声音源的空间坐标。基于时延估计(TDOA)

YOLO 672

【SpringBoot实战专题】「开发实战系列」全方位攻克你的技术盲区之Spring定义Jackson转换Null的方法和实现案例

自定义的,它是一个继承自的序列化器,用于处理值为null的情况。在serialize方法中,我们将使用自定义的对象来替代值为null的字段进行序列化。因此,我们可以通过自定义ObjectMapper和注册自定义的序列化器来在转换过程中添加我们需要的控制逻辑。上述的示例代码展示了如何在WebConfig配置类中实现这一点,通过注册自定义的序列化器实现对特定字段null值的转换为默认值。在类中,我们定义了一个包含一个名为def的列表字段,用于存储默认值。

世界上并没有完美的程序,但是我们并不因此而沮丧,因为写程序就是一个不断追求完美的过程。 1514

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

springboot对象序列化自定义序列化注解;自定义null值序列化;序列化时获取字段上的注解信息

springboot对象序列化自定义序列化注解

奔跑的菜鸡 1398

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

jackson注解详细说明

jackson的maven依赖 <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.3</version&g...

大鹏小站的博客 2282

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

java jackson 自定义_java – 用于自定义NULL值序列化的Jackson自定...

根据这个答案:https://stackoverflow.com/a/43342675/5810648我写了这样的序列化器:public class CustomSerializer extends StdSerializer implements ContextualSerializer {private final NAifNull annotation;public CustomSerial...

weixin_42511832的博客 228

java属性为null,序列化时去掉这个属性

第一种方法: @JsonInclude(JsonInclude.Include.NON_NULL) 将这个注解加在实体类对应的对象名上面,或者类名上面。 Include.ALWAYS 属性都序列化 Include.NON_DEFAULT 属性为默认值不序列化 Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化 Include.NON_NULL 属性为NULL 不序列化 如: private Integer id; @JsonInclude(JsonInclude.

a984171281的博客 6751

关于json序列化

Java对象在转json的时候,如果对象里面有属性值为null的话,那么在json序列化的时候要不要序列出来呢?对比以下json转换方式。

jcc4261的博客 1893

详细分析Java中的@JsonSerialize注解

`@JsonSerialize` 是 Jackson 库中的一个注解,用于指定在将 Java 对象序列化为 JSON 格式时,如何进行自定义的序列化处理。

码农研究僧的博客 6372

@JsonSerialize注解的使用

自定义序列化

KKK 1万+

Jackson,实现Bean和JSON之间的灵活转换(SpringMVC默认的JSON转换器)

Jackson介绍 Jackson是Java最受欢迎的JSON类库之一,包含两个不同的解析器: Jackson ObjectMapper,将JSON转化为Java对象,或者转换为Jackson特定的树结构 Jackson JsonParser,JSON流解析器,每次只解析一个JSON标记(不做讲解) Jackson还包含两个不同的生成器: Jackson ObjectMapper,可以从Java对象生成JSON,或者是从Jackson的树结构生成JSON Jackson Generator,每次生成一

qq_43842093的博客 2976

Java接口响应序列化时使用自定义注解特殊处理数据

在接口响应时,有很多情况需要做数据转换,比如数据脱敏,null值给默认,数据类型转换...,那怎么避免在代码里写过多的 if 判断和处理逻辑呢?那么使用jackson内省器,在组装json的时候处理想要的数据,就会避免使用切面时遇到的问题。Java 接口响应数据都是使用jackson作为序列化框架的,其中有一个对象。NopAnnotationIntrospector:字面意思 “注释内省器”如果数据有多层,就需要自己写循环迭代处理,比较麻烦。数据处理后,类型必须要跟对象的属性类型一样。

weixin_45738731的博客 860

Jackson 解析json的一些用法

Jackson 解析json的一些用法

timchen525的专栏 1426

将Integer类型的字段如果是null,展示0

Integer类型的字段如果是null,展示0

CelineT的博客 4238
上一篇: git 新建项目本地无法推送到远程
下一篇: Java 与 Rust 性能对比
Chengdu.S
博客等级 码龄9年 282粉丝 86原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chengdu.S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值