目录

  • 问题引入
  • 解决问题
    • 查看 @JsonSerialize(nullsUsing = StringNullSerializer.class) nullsUsing 的实现逻辑
    • 自定义注解解决问题
    • 如何整合到 SpringBoot 项目
    • 项目地址

问题引入

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> {@Overridepublic 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();@Testpublic 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();@Testpublic 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);}@Testpublic 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*/@Overridepublic 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

欢迎大家关注我的公众号

Jackson 序列化 自定义注解处理Null 值相关推荐

  1. Jackson 通过自定义注解来控制json key的格式

    Jackson 通过自定义注解来控制json key的格式 最近我这边有一个需求就是需要把Bean中的某一些特殊字段的值进行替换.而这个替换过程是需要依赖一个第三方的dubbo服务的.为了使得这个转换 ...

  2. oracle根据null排序,oracle 关于null值排序

    在oracle中根据字段来desc排序的话null值可能会在数据的最前面.然而有时候我们查看数据的时候并不希望能够在前面看到这些null值的排序数据. 因此我查了一下: 1.排序的时候运用nvl(). ...

  3. Java通过自定义注解执行方法_Java自定义注解(使用篇)

    TL;DR Java 注解广泛运用在开发之中,用于增强变量/方法/类等. 尝试说明 Java 自定义注解的使用,以及通过开源项目中的使用进行说明. 本文主要记录个人的理解,全文基于Java SE8. ...

  4. hutol json null值没了_JSON数据处理框架Jackson精解第一篇-序列化与反序列化核心用法...

    Jackson是Spring Boot默认的JSON数据处理框架,但是其并不依赖于任何的Spring 库.有的小伙伴以为Jackson只能在Spring框架内使用,其实不是的,没有这种限制.它提供了很 ...

  5. java获取注解的属性值_反射+自定义注解,实现获取注解标记的属性

    目标:通过自定义注解 @Ignore 注解,觉得是否读取指定类的属性. 运行结果: [main] INFO util.FruitInfoUtil -水果的名字为:entity.Apple [main] ...

  6. java 自定义注解以及获得注解的值

    1.自定义注解 import java.lang.annotation.*;@Documented @Target(ElementType.FIELD) @Inherited @Retention(R ...

  7. @value注解取不到值_教学笔记:Java注解及自定义注解示例

    现代的Java编程过程中,会经常需要使用到注解,各种流行框架,比如在使用spring进行应用构建的过程中会使用到非常多的spring注解. 本文简要谈一谈Java注解以及如何去定义自己的注解在程序中进 ...

  8. 有属性的自定义注解,如何获取到post请求中RequestBody中对象的一个属性值?

    1,写两个自定义注解,一个作用于方法的,一个作用于字段的 作用于方法的自定义注解代码: package com.youku.nintendo.annotation;import enums.Permi ...

  9. mybatis入门学习(九) -DB环境切换、使用注解、事务提交、获取自增ID、多参数传参、鉴别器、内置参数、批量写入、Oracle字段无法插入 null 值

    一.多数据库环境切换 1.config配置: <!-- default="mydemo" 指定连接的数据库 --><environments default=&q ...

最新文章

  1. 英语语法---感叹词详解
  2. Leetcode--210.课程表Ⅱ
  3. 基于STM32L476的锂电池SOC检测
  4. java main函数_一行JAVA代码如何运行起来?
  5. IOC与DI(xml 配置)
  6. input美化上传按钮美化
  7. *最近培训的一个题目:蚂蚁爬竿
  8. OSPF综合实验(有点难哦!)
  9. python图片中文字识别
  10. 台式计算机运行慢怎么样能提高速度,电脑配置低怎么办 如何让电脑运行速度加快...
  11. 上传声音 微信小程序_微信小程序实现录制、试听、上传音频功能(带波形图)...
  12. 关于下载《Java程序员,上班那点事儿》的电子版
  13. startx 及xinit 介绍(经典)
  14. 【AdaBoost算法】
  15. JAVA基础加强篇08——集合
  16. Agile Office 365 免积分下载
  17. Bootstrap后台导航
  18. 如何打造一支无法跨部门合作的团队?
  19. linux 修改sh文件生效,Linux-shell脚本基础
  20. UML总结之九种模型图

热门文章

  1. electorn 白屏解决方案(偶发性白屏)
  2. LeetCode:面试题 01.02. 判定是否互为字符重排————简单
  3. dnf 跨服 服务器 位置,《DNF》86版本跨区表一览 看你属于哪个跨区频道
  4. 这不仅仅是另一个使用TensorFlow来做MNIST数字图像识别的教程
  5. 六级(2020/12-1) Section B
  6. 大量精品中医古籍下载
  7. 2022新H5拼团抽奖拆盲盒模式源码+功能非常强大
  8. 免校准的电量计量芯片_电量计量芯片HLW8110的前端电路设计与误差分析校正
  9. selenium_3种等待以及unittest测试框架_智能等待
  10. 基于Python的网上订餐系统的设计与实现