SpringBoot 定制化返回数据的实现示例

 更新时间:2020年07月15日 08:32:18   作者:阿亮叨逼叨  
这篇文章主要介绍了SpringBoot 定制化返回数据的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

此时我们的返回结构如下:

{
  "code": 200,
  "msg": "ok",
  "data": {
    "id": 1,
    "username": "steve",
    "secretKey": "xxx",
    "expiredAt": null,
    "createdAt": "2020-07-07T06:09:15"
  }
}

但上面有几个问题:

  •  我希望字段是以下划线命名方式,也就是 createdAt 改成 created_at 这样
  •  我希望某些字段值的输出格式可以自定义,比如日期类型我希望输出是 yyyy-MM-dd HH:mm:ss
  •  我不希望 secretKey 这类具有安全性质的字段返回给调用方
  •  我不希望有 null 这样的输出,避免给调用方不必要的麻烦

定制字段名

我们有两种选择,第一种是在每一个字段上通过添加 @JsonProperty 注解来实现,如下:

@JsonProperty("secret_key")
private String secretKey;

这种方式灵活度高,缺点就是繁琐,变量名是单个单词的不用转换,多个单词的如果要保持统一格式就需要每个都写上,工作量不小。

第二种方式就是全局配置 Spring 内置的 Jackson 的序列化转换器,在 config 目录下新建 JsonConfig.java 文件:

package com.foxescap.wxbox.config;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * @author xfly
 */
@EnableWebMvc
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

  @Bean
  public LocalDateTimeSerializer localDateTimeSerializer() {
    return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  }

  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(
        new MappingJackson2HttpMessageConverter(
            new Jackson2ObjectMapperBuilder()
                .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
                .build()
        )
    );
  }
}

我们通过重写 WebMvcConfigurer 接口的 configureMessageConverters 方法,添加自定义的 JSON 转换器,关键是 propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) 这行代码,设置属性的命名策略为下划线命名方式。

定制字段值格式

最常见的就是对时间类型的字段格式化,也有两种方式,第一种是在每个字段上添加 @JsonFormat 注解,比如格式化日期时间:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime expiredAt;

也可以全局配置,我们在上面 JsonConfig 代码的基础上,加上一个类型串行器:

@Bean
public LocalDateTimeSerializer localDateTimeSerializer() {
  return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  converters.add(
    new MappingJackson2HttpMessageConverter(
      new Jackson2ObjectMapperBuilder()
      .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
      .serializerByType(LocalDateTime.class, localDateTimeSerializer())
      .build()
    )
  );
}

这样就能对全局 LocalDateTime 类型的字段序列化时转换成我们自定义的格式了。

定制可见性

当我们不需要有字段被序列化,即需要忽略它,那么可以在那个字段上添加 @JsonIgnore 注解即可。

处理 Null

一般地,要么是直接忽略值为 null 的字段,要么是将 null 转换成空字符串处理,前者可以直接在每个需要的字段上加 @JsonInclude(Include.NON_NULL) 注解,或者也可以在每个需要序列化的类上加,当然也可以全局配置,在 .build() 前加入 .serializationInclusion(JsonInclude.Include.NON_NULL) 即可。

如果我们不希望 null 值直接被忽略,又不需要直接给调用方返回 null,那么可以添加一个 setNullValueSerializer 方法自定义输出:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  var builder = new Jackson2ObjectMapperBuilder()
    .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
    //.serializationInclusion(JsonInclude.Include.NON_NULL)
    .serializerByType(LocalDateTime.class, localDateTimeSerializer())
    .build();

  builder.getSerializerProvider()
    .setNullValueSerializer(new JsonSerializer<>() {
      @Override
      public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString("");
      }
    });
  converters.add(new MappingJackson2HttpMessageConverter(builder));
}

纠结过是直接不序列化 Null 值还是设为空值,考虑到对于调用方,如果直接将 Null 值忽略了的话,数据的结构完整性就大大破坏了,比如一个数组,有几个数组元素里的字段有,有几个没有,对于调用方就非常不友好了。

如果你想对不同变量类型的 Null 值分别处理的话,那么就需要重写 changeProperties 方法,比如对于数组集合类型的字段,如果是 Null 值则序列化成 [] ;如果是字符串类型的字段,序列化成 "" ;如果是不二类型的字段,序列化成 false 等等:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  var builder = new Jackson2ObjectMapperBuilder()
    .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
    .serializerByType(LocalDateTime.class, localDateTimeSerializer())
    .build();

  builder.setSerializerFactory(builder.getSerializerFactory().withSerializerModifier(new BeanSerializerModifier() {
    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
      for (var beanPropertyWriter : beanProperties) {
        var javaType = beanPropertyWriter.getType();
        if (javaType.isArrayType() || javaType.isCollectionLikeType()) {
          beanPropertyWriter.assignNullSerializer(new JsonSerializer<>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
              jsonGenerator.writeStartArray();
              jsonGenerator.writeEndArray();
            }
          });
        } else if (javaType.isTypeOrSubTypeOf(String.class)) {
          beanPropertyWriter.assignNullSerializer(new JsonSerializer<>() {
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
              gen.writeString("");
            }
          });
        } else if (javaType.isTypeOrSuperTypeOf(Boolean.class)) {
          beanPropertyWriter.assignNullSerializer(new JsonSerializer<>() {
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
              gen.writeBoolean(false);
            }
          });
        } else if (javaType.isMapLikeType()) {
          beanPropertyWriter.assignNullSerializer(new JsonSerializer<>() {
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
              gen.writeStartObject();
              gen.writeEndObject();
            }
          });
        } else if (javaType.isTypeOrSuperTypeOf(Integer.class) ||
              javaType.isTypeOrSuperTypeOf(Long.class) ||
              javaType.isTypeOrSuperTypeOf(Double.class) ||
              javaType.isTypeOrSuperTypeOf(Float.class)) {
          beanPropertyWriter.assignNullSerializer(new JsonSerializer<>() {
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
              gen.writeNumber(0);
            }
          });
        } else if (javaType.isTypeOrSuperTypeOf(LocalDateTime.class) ||
              javaType.isTypeOrSuperTypeOf(LocalDate.class)) {
          beanPropertyWriter.assignNullSerializer(new JsonSerializer<>() {
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
              gen.writeString("");
            }
          });
        }
      }

      return beanProperties;
    }
  }));

  converters.add(new MappingJackson2HttpMessageConverter(builder));
}

到此这篇关于SpringBoot 定制化返回数据的实现示例的文章就介绍到这了,更多相关SpringBoot 定制化返回数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解使用@RequestBody取POST方式的json字符串

    详解使用@RequestBody取POST方式的json字符串

    这篇文章主要介绍了详解使用@RequestBody取POST方式的json字符串,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • 基于spring mvc请求controller访问方式

    基于spring mvc请求controller访问方式

    这篇文章主要介绍了spring mvc请求controller访问方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java垃圾回收机制的finalize方法实例分析

    Java垃圾回收机制的finalize方法实例分析

    这篇文章主要介绍了Java垃圾回收机制的finalize方法,结合实例形式分析了finalize方法的特点及在垃圾回收机制中的相关操作技巧,需要的朋友可以参考下
    2019-08-08
  • JVM内存飙升线上问题排查方式

    JVM内存飙升线上问题排查方式

    文章主要介绍了线上CMS服务内存增长问题的排查过程,通过分析GC日志和堆栈快照,定位问题为Nacos的NamingService对象无法回收和MySQL的CallableStatement对象增长迅速,最终通过将NamingService改为单例模式解决了内存增长问题
    2025-03-03
  • Java线程让步_动力节点Java学院整理

    Java线程让步_动力节点Java学院整理

    yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权。下面通过本文给大家介绍Java线程让步的相关知识,需要的朋友参考下吧
    2017-05-05
  • Java多线程并发编程 Volatile关键字

    Java多线程并发编程 Volatile关键字

    volatile 关键字是一个神秘的关键字,也许在 J2EE 上的 JAVA 程序员会了解多一点,但在 Android 上的 JAVA 程序员大多不了解这个关键字。只要稍了解不当就好容易导致一些并发上的错误发生,例如好多人把 volatile 理解成变量的锁
    2017-05-05
  • Java实现Web应用中的定时任务(实例讲解)

    Java实现Web应用中的定时任务(实例讲解)

    下面小编就为大家分享一篇Java实现Web 应用中的定时任务的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-11-11
  • java WebSocket 服务端实现代码

    java WebSocket 服务端实现代码

    WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端,这篇文章主要介绍了java WebSocket 服务端代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-02-02
  • springboot logback如何从apollo配置中心读取变量

    springboot logback如何从apollo配置中心读取变量

    这篇文章主要介绍了springboot logback如何从apollo配置中心读取变量的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • IntelliJ IDEA创建普通的Java 项目及创建 Java 文件并运行的教程

    IntelliJ IDEA创建普通的Java 项目及创建 Java 文件并运行的教程

    这篇文章主要介绍了IntelliJ IDEA创建普通的Java 项目及创建 Java 文件并运行的教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02

最新评论