写点什么

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

作者:洛神灬殇
  • 2024-01-13
    江苏
  • 本文字数:4801 字

    阅读完需:约 16 分钟

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

背景

在 Spring MVC 中配置 JSON 转换器时,当需要全局控制将某些 JSON 返回值的 key 转换为一些默认值时,可以通过实现一个自定义的序列化器(Serializer)来实现这个功能。现在,我们来深入了解 Spring MVC 的整体处理流程,以确定在哪个步骤进行实现和调整。



我们先来总结数量一下整个流程:


  1. 用户发送请求,由 Spring 的前端控制 Servlet DispatcherServlet 捕获。

  2. DispatcherServlet 解析请求 URL,并根据 URI 获取与之相关的 Handler 和拦截器配置。

  3. DispatcherServlet 选择合适的 HandlerAdapter 来执行 Handler(Controller)。在此过程中,也会执行拦截器的 preHandler 方法。

  4. 提取请求中的模型数据(通过 MessageConverter 对于入参进行转换)并填充 Handler 的参数,开始执行 Handler。

  5. 执行完成后,返回一个包含 Model 和 View 信息的 ModelAndView 对象。

  6. 根据 ModelAndView 选择合适的 ViewResolver,将其返回给 DispatcherServlet。

  7. ViewResolver 将 Model 和 View 结合起来,进行视图渲染。

  8. 最终将渲染结果返回给客户端(响应的数据转换则通过 MessageConverter 完成)

MessageConverter

对于 JSON 转换,涉及到 MessageConverter 的工作,通过在 Spring MVC 处理流程中的适当步骤实现自定义序列化器,我们能够全局控制 JSON 返回值的 key,确保它们在需要时转换为指定的默认值。这种方式提供了灵活性和可定制性,使开发者能够更好地适应不同的业务需求。



MappingJackson2HttpMessageConverter 是 Spring 框架中提供的一个 HTTP 消息转换器,用于在 Spring MVC 中进行 JSON 与 Java 对象之间的转换。它基于 Jackson 库,利用 Jackson 提供的功能进行 JSON 的序列化和反序列化,接下来我们就针对于它进行实现。

使用 Jackson 原生方式处理空字段(次重点方案)

当返回字段为 null 时需要进行处理,可以通过配置 ObjectMapper 来选择合适的处理方式。以下是几种常见的处理方式及其配置方式:


ObjectMapper 的配置选项

使得 ObjectMapper 在将 Java 对象转换为 JSON 字符串时,仅包含字段值不为 null 的属性。对于为 null 的属性,它们将被忽略并不会显示在生成的 JSON 中。


// 创建ObjectMapper实例ObjectMapper objectMapper = new ObjectMapper();// 配置ObjectMapper处理返回字段为null的方式objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);  // 字段均包含在结果JSON中,无论是否为nullobjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);  // 字段的值不为null时,才会包含在结果JSON中objectMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);  // 字段的值与其默认值不相等时,才会包含在结果JSON中objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);  // 字段的值不为null且不为空时,才会包含结果JSON中// 使用配置好的ObjectMapper进行对象转换YourObject yourObject = ...;  // 要转换的对象String json = objectMapper.writeValueAsString(yourObject);  // 将对象转换为JSON字符串
复制代码

通过使用注解的方式

使用注解可以更精确地控制每个类或属性的处理方式。


@JsonInclude(JsonInclude.Include.ALWAYS)@JsonInclude(JsonInclude.Include.NON_NULL)@JsonInclude(JsonInclude.Include.NON_DEFAULT)@JsonInclude(JsonInclude.Include.NON_EMPTY)
复制代码


通过调用 setSerializationInclusion 方法并传入相应的 JsonInclude.Include 枚举值,可以配置 ObjectMapper 处理返回字段为 null 时的方式。可以根据具体需求选择适合的处理方式。

MappingJackson2HttpMessageConverter(重点方案)

对于 SpringMVC,我们会采用 MappingJackson2HttpMessageConverter 可以将 Java 对象转换为 JSON 格式的响应体,并将 JSON 格式的请求体转换为对应的 Java 对象。它支持多种媒体类型,如 application/json、text/json 等。

创建 MappingJackson2HttpMessageConverter

在 Spring MVC 中配置 MappingJackson2HttpMessageConverter 可以通过以下两种方式:Xml 配置和 Javaconfig 配置

XML 配置

下面展示了如何在 applicationContext.xml 文件中进行 XML 配置来实现对 MappingJackson2HttpMessageConverter 的定义,并通过<property>元素设置了 supportedMediaTypes 属性:


<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">    <property name="supportedMediaTypes">        <list>            <value>application/json</value>            <!-- 可以添加其他支持的媒体类型 -->        </list>    </property>    <!-- 其他配置属性 --></bean>
复制代码


在上述配置中,我们使用<bean>元素定义了 MappingJackson2HttpMessageConverter 的 bean,并指定了其类名 org.springframework.http.converter.json.MappingJackson2HttpMessageConverter,通过<property>元素来设置 supportedMediaTypes 属性。通过<list>元素来指定一个媒体类型列表,其中每个类型由<value>元素表示。在示例中,我们设置了一个值为 application/json 的媒体类型。这样就可以将 Java 对象转换为 JSON,并以指定的媒体类型返回给客户端。

Java 配置

在 Java 配置中,我们可以使用 @Bean 注解创建 MappingJackson2HttpMessageConverter 的 bean,并在方法中进行相同的配置。此外,我们还可以使用 Charset.forName("UTF-8")来替代 java.nio.charset.StandardCharsets.UTF_8 来创建 MediaType。具体示例如下:


@Configurationpublic class WebConfig extends WebMvcConfigurerAdapter {    @Override    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();        // 配置媒体类型        converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON));        // 其他配置...        converters.add(converter);    }}
复制代码

MappingJackson2HttpMessageConverter 整合 ObjectMapper 的配置

在上面我们定义了一个 MappingJackson2HttpMessageConverter bean,并设置了 supportedMediaTypes 属性为"application/json",以支持在 Spring MVC 中将 Java 对象转换为 JSON 并返回给客户端。


在这个基础知识,我们加入了最早介绍的 ObjectMapper bean,并设置其 serializationInclusion 属性为 NON_NULL,这将使得序列化时排除掉值为 null 的字段。

XML 配置

<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">    <property name="supportedMediaTypes">        <list>            <value>application/json;charset=UTF-8</value>        </list>    </property>    <property name="objectMapper">        <bean class="com.fasterxml.jackson.databind.ObjectMapper">            <property name="serializationInclusion">                <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>            </property>        </bean>    </property></bean>
复制代码

JavaConfig 配置

通过 mappingJacksonHttpMessageConverter 方法创建了一个 MappingJackson2HttpMessageConverter 的实例,并设置了支持的媒体类型为 application/json;charset=UTF-8。


@Configurationpublic class WebConfig {
@Bean public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); // 配置支持的媒体类型为application/json;charset=UTF-8 converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8)); // 配置ObjectMapper,设置serializationInclusion为NON_NULL ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); converter.setObjectMapper(objectMapper); return converter; }}
复制代码


创建了一个 ObjectMapper 实例,并配置了其 serializationInclusion 为 NON_NULL,然后将该 ObjectMapper 设置到 MappingJackson2HttpMessageConverter 中。

自定义的 null 处理类注解

自定义的 DetfaultNullJsonSerializer,它是一个继承自 JsonSerializer<Object> 的序列化器,用于处理值为 null 的情况。在 serialize 方法中,我们将使用自定义的 JsonNullDef 对象来替代值为 null 的字段进行序列化。



public class DetfaultNullJsonSerializer extends JsonSerializer<Object> { @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeObject(new JsonNullDef()); }}
class JsonNullDef { private List<String> def = new ArrayList<>(); public List<String> getDef() { return def; } public void setDef(List<String> def) { this.def = def; }}
复制代码


因此,我们可以通过自定义 ObjectMapper 和注册自定义的序列化器来在转换过程中添加我们需要的控制逻辑。上述的示例代码展示了如何在WebConfig配置类中实现这一点,通过注册自定义的序列化器实现对特定字段 null 值的转换为默认值。


JsonNullDef 类中,我们定义了一个包含一个名为 def 的列表字段,用于存储默认值。通过这个自定义的 null 处理类,我们可以在值为 null 的字段处提供一个默认值,从而避免在序列化时出现 null 值。

方案 1:使用注解

在需要使用自定义序列化器的类或对象上,通过注解或方法来指定使用 DetfaultNullJsonSerializer,例如在 MyData 类中使用注解:


import com.fasterxml.jackson.databind.annotation.JsonSerialize;public class MyData {    @JsonSerialize(nullsUsing = DetfaultNullJsonSerializer.class)    private String name;    @JsonSerialize(nullsUsing = DetfaultNullJsonSerializer.class)    private String description;    // 其他字段和方法...   // 构造函数、getter和setter方法...}
复制代码


除了直接用到了注解上,还可以定义到序列化器上面,方便控制和整合到 MappingJackson2HttpMessageConverter 中。

方案 1:序列化器

创建一个自定义的 ObjectMapper 并注册自定义的序列化器:



@Configurationpublic class WebConfig { public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(Object.class, new DetfaultNullJsonSerializer()); objectMapper.registerModule(module); return objectMapper; } @Bean public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); // 配置支持的媒体类型为application/json;charset=UTF-8 converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8)); converter.setObjectMapper(objectMapper()); return converter; }}
复制代码

总结一下

用自定义的 DetfaultNullJsonSerializer,在类或属性上使用 @JsonSerialize(nullsUsing = DetfaultNullJsonSerializer.class)注解指定使用该序列化器,以及在配置类中创建并注册自定义的 ObjectMapper,将自定义序列化器与 ObjectMapper 关联。在代码中使用该 ObjectMapper 实现对象的序列化和反序列化操作。这样,你就可以灵活地控制值为 null 的字段的处理方式。

发布于: 刚刚阅读数: 5
用户头像

洛神灬殇

关注

🏆 InfoQ写作平台-签约作者 🏆 2020-03-25 加入

👑 后端技术架构师,前优酷资深工程师 📕 个人著作《深入浅出Java虚拟机—JVM原理与实战》 💻 10年开发经验,参与过多个大型互联网项目,定期分享技术干货和项目经验

评论

发布
暂无评论
【SpringBoot实战专题】「开发实战系列」全方位攻克你的技术盲区之Spring定义Jackson转换Null的方法和实现案例_Java_洛神灬殇_InfoQ写作社区