背景

如今炒的火热的前后端分离项目,大多数开发人员选择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.

*

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 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 {

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 handledType() {

return LocalDateTime.class;

}

}

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

可以看到返回前端的是序列化后的格式,其他类型如Date,ZonedDateTime等等都可以使用这种方式序列化和反序列化

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: