IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 一起聊聊springboot实现RESTful API中json格式,自定义序列号和反序列号规则 -> 正文阅读

[Java知识库]一起聊聊springboot实现RESTful API中json格式,自定义序列号和反序列号规则

背景

  • 如今炒的火热的前后端分离项目,大多数开发人员选择RESTful设计风格,Java Web 人员经常要设计 RESTful API,这种设计通常使用 json 数据进行交互。那么前端传入的 json 数据如何序列化成 Java 对象,后端返回的结果又如何将Java 对象解析成 json 格式数据返回给前端。
  • spring-boot-stater-web依赖的json解析是jackson,同时也为我们进行了jackson的一系列自动化配置,这样我们不需要导入其他json依赖,就可以直接使用,其中起到关键作用的是MappingJackson2HttpMessageConverter
  • 在整个解析过程,HttpMessageConverter 起到了重要作用,各大公司实现了自己的HttpMessageConverter,如:
    jackson的MappingJackson2HttpMessageConverter;
    gson的GsonHttpMessageConverter;
    fastjson的FastJsonHttpMessageConverter等等。

问题

数据库数据类型datetime,java类型为LocalDateTime,我们来看看springboot默认返回什么时间格式给前端

在这里插入图片描述
可以看到,是世界时间格式,因为LocalDateTime类型是和时区有关的时间,"2022-03-07T15:09:19"格式,T就是表示UTC的世界时间,但是我们并不需要这种格式,我们不想把T返回或者我们只需要返回日期,怎么办呢

源码分析

刚刚我们讲到spring-boot-stater-web依赖的json解析是jackson,其中起到关键作用的是MappingJackson2HttpMessageConverter

@Configuration
@ConditionalOnClass({ObjectMapper.class})
@ConditionalOnBean({ObjectMapper.class})
@ConditionalOnProperty(
    name = {"spring.http.converters.preferred-json-mapper"},
    havingValue = "jackson",
    matchIfMissing = true
)
protected static class MappingJackson2HttpMessageConverterConfiguration {
    protected MappingJackson2HttpMessageConverterConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        value = {MappingJackson2HttpMessageConverter.class},
        ignoredType = {"org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter", "org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter"}
    )
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        return new MappingJackson2HttpMessageConverter(objectMapper);
    }
}

可以看到这里做了MappingJackson2HttpMessageConverter 类的bean声明,会依赖ObjectMapper 注入

/**
 * Construct a new {@link MappingJackson2HttpMessageConverter} with a custom {@link ObjectMapper}.
 * You can use {@link Jackson2ObjectMapperBuilder} to build it easily.
 * @see Jackson2ObjectMapperBuilder#json()
 */
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
  super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}

我们看下MappingJackson2HttpMessageConverter的构造方法,为json类型设置ObjectMapper 映射关系,可以自定义ObjectMapper,可以通过Jackson2ObjectMapperBuilder快速构建ObjectMapper

我们来看下Jackson2ObjectMapperBuilder

构建常规 JSON {@link ObjectMapper} 实例。

/**
 * Obtain a {@link Jackson2ObjectMapperBuilder} instance in order to
 * build a regular JSON {@link ObjectMapper} instance.
 */
public static Jackson2ObjectMapperBuilder json() {
	return new Jackson2ObjectMapperBuilder();
}

自定义反序列化规则

/**
 * Configure custom deserializers. Each deserializer is registered for the type
 * returned by {@link JsonDeserializer#handledType()}, which must not be {@code null}.
 * @since 4.3
 * @see #deserializersByType(Map)
 */
public Jackson2ObjectMapperBuilder deserializers(JsonDeserializer<?>... deserializers) {
	for (JsonDeserializer<?> deserializer : deserializers) {
		Class<?> handledType = deserializer.handledType();
		if (handledType == null || handledType == Object.class) {
			throw new IllegalArgumentException("Unknown handled type in " + deserializer.getClass().getName());
		}
		this.deserializers.put(deserializer.handledType(), deserializer);
	}
	return this;
}

自定义序列化规则

/**
 * Configure custom serializers. Each serializer is registered for the type
 * returned by {@link JsonSerializer#handledType()}, which must not be {@code null}.
 * @see #serializersByType(Map)
 */
public Jackson2ObjectMapperBuilder serializers(JsonSerializer<?>... serializers) {
	for (JsonSerializer<?> serializer : serializers) {
		Class<?> handledType = serializer.handledType();
		if (handledType == null || handledType == Object.class) {
			throw new IllegalArgumentException("Unknown handled type in " + serializer.getClass().getName());
		}
		this.serializers.put(serializer.handledType(), serializer);
	}
	return this;
}

构建ObjectMapper,并设置相关规则

/**
 * Build a new {@link ObjectMapper} instance.
 * <p>Each build operation produces an independent {@link ObjectMapper} instance.
 * The builder's settings can get modified, with a subsequent build operation
 * then producing a new {@link ObjectMapper} based on the most recent settings.
 * @return the newly built ObjectMapper
 */
@SuppressWarnings("unchecked")
public <T extends ObjectMapper> T build() {
	ObjectMapper mapper;
	if (this.createXmlMapper) {
		mapper = (this.defaultUseWrapper != null ?
				new XmlObjectMapperInitializer().create(this.defaultUseWrapper, this.factory) :
				new XmlObjectMapperInitializer().create(this.factory));
	}
	else {
		mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());
	}
	configure(mapper);
	return (T) mapper;
}

全局配置方式

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

@Configuration
public class ObjectMapperConfig {

    @Bean
    public ObjectMapper jsonObjectMapper(){
        Jackson2ObjectMapperBuilder objectMapperBuilder = Jackson2ObjectMapperBuilder.json();

        //objectMapperBuilder.serializers(new CustomDateSerialize());
        objectMapperBuilder.serializers(new CustomLocalDateTimeSerialize());

        return objectMapperBuilder.build();
    }

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

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class CustomLocalDateTimeSerialize extends JsonSerializer<LocalDateTime> {
    private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
    private DateTimeFormatter sdf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneId.systemDefault());

    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        String localTime = df.format(localDateTime);
        jsonGenerator.writeString(localTime);
    }

    @Override
    public Class<LocalDateTime> handledType() {
        return LocalDateTime.class;
    }
}

我们看看自定义序列化后的结果

在这里插入图片描述
可以看到返回前端的是序列化后的格式,其他类型如DateZonedDateTime等等都可以使用这种方式序列化和反序列化

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 18:38:23  更:2022-07-20 18:40:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 7:24:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码