目录
引文:
我们在开发过程当中,经常会在接口响应结果中以 Json 的形式返回数据,我们也对于这种处理方式习以为常。那么大家是否想过,SpringBoot 当中将对象转为 Json 格式有几种方式?本文将介绍开发中常用的三种 Json 序列化的方式:Jackson、FastJSON、Gson。
JSON 文档:
- JSON 中文官网:http://www.json.org/json-zh.html
- JSON 官网:http://www.json.org/
一、Jackson 方案(SpringBoot默认支持)
Jackson
是用来序列化和反序列化 json 的 Java 开源框架。
1.1 Jackson 库的特点
Jackson 库具有以下特点:
- Spring MVC 的默认 json 解析器就是 Jackson。
- 与其他 Java 的 json 框架 Gson 等相比,Jackson 解析大的 json 文件速度比较快。
- Jackson 运行时内存比较低,性能比较好。
- Jackson 有灵活的 API,容易扩展和定制。
补充:SpringBoot 中 Jackson 库的依赖集成在
spring-boot-starter-web
组件中,具体位置如下图所示:
1.2 Jackson 的核心模块
Jackson 的核心模块由三部分组成:
jackson-core
:核心包,提供基于 “流模式” 解析的相关 API,它包括 JsonParser 和 JsonGenerator。Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。jackson-annotations
:注解包,提供标准注解功能。jackson-databind
:数据绑定包,提供基于 “对象绑定” 解析的相关 API(ObjectMapper)和 “树模型” 解析的相关 API(JsonNode);基于 “对象绑定” 解析的 API 和 “树模型” 解析的 API 依赖基于 “流模式” 解析的 API。
在了解 Jackson 的概要情况之后,下面介绍 Jackson 的基本用法。
1.3 Maven依赖
<!-- Jackson库 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.5</version><scope>compile</scope></dependency><!-- Jackson 支持 LocalDateTime 格式化 --><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.13.5</version><scope>compile</scope></dependency>
jackson-databind
依赖包含了
jackson-core
和
jackson-annotations
。当添加了 jackson-databind 之后,jackson-core 和 jackson-annotations 也就添加到 Java 项目工程中了。在添加相关依赖包之后,就可以使用 Jackson。
1.4 代码示例
Jackson 最常用的 API 就是基于 “对象绑定” 的
ObjectMapper
。下面是一个简单的使用示例:
Person.java
importcom.fasterxml.jackson.annotation.JsonFormat;importcom.fasterxml.jackson.databind.annotation.JsonDeserialize;importcom.fasterxml.jackson.databind.annotation.JsonSerialize;importcom.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;importcom.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;importlombok.Data;importjava.time.LocalDateTime;@DatapublicclassPerson{// 字符串测试privateString name;// 空对象测试privateInteger age;// 默认值测试privateint height;// 日期测试@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone="GMT+8")@JsonSerialize(using =LocalDateTimeSerializer.class)@JsonDeserialize(using =LocalDateTimeDeserializer.class)privateLocalDateTime createTime;}
JacksonTest.java
importcom.demo.model.Person;importcom.fasterxml.jackson.databind.ObjectMapper;importjava.io.IOException;importjava.time.LocalDateTime;/**
* <p> @Title JacksonTest
* <p> @Description Jackson库测试
*
* @author ACGkaka
* @date 2024/4/12 19:04
*/publicclassJacksonTest{publicstaticvoidmain(String[] args)throwsIOException{ObjectMapper mapper =newObjectMapper();// 造数据Person person =newPerson();
person.setName("Tom");
person.setAge(40);
person.setCreateTime(LocalDateTime.now());// 序列化String jsonString = mapper.writeValueAsString(person);System.out.println("序列化结果(JSON):"+ jsonString);// 反序列化Person deserializedPerson = mapper.readValue(jsonString,Person.class);System.out.println("反序列化结果(toString):"+ deserializedPerson);}}
执行结果:
1.5 LocalDateTime 格式化
在上面的示例中,我们可以看到 LocaDateTime 的属性上面使用了 @JsonFormat、@JsonSerialize、@JsonDeserialize 三个注解,它们具体是什么作用呢?
@JsonFormat
:重点关注日期时间类型的序列化与反序列化 格式。@JsonSerialize
:指定自定义序列化逻辑,重点关注 序列化时的定制化处理。@JsonDeserialize
:执行自定义反序列化逻辑,重点关注 反序列化时的定制化处理。
补充: 如果只想在 RESTful 接口返回正确的时间格式,只需要使用
@JsonFormat
即可,但是如果想在代码中使用 ObjectMapper 进行序列化的话就需要使用
@JsonFormat
+
@JsonSerialize
。
在 Jackson 的 2.13.5 版本中,如果依赖中只集成了
jackson-databind
依赖,没有集成
jackson-datatype-jsr310
依赖,是无法自动将 LocalDateTime 类型的字段进行序列化的,报错如下:
- Java 8 date/time type
java.time.LocalDateTime
not supported by default: add Module “com.fasterxml.jackson.datatype:jackson-datatype-jsr310” to enable handling (through reference chain: com.demo.model.Person[“createTime”])
完整报错如下:
Exception in thread "main"com.fasterxml.jackson.databind.exc.InvalidDefinitionException:Java8 date/time type `java.time.LocalDateTime` not supported by default: add Module"com.fasterxml.jackson.datatype:jackson-datatype-jsr310"toenable handling (through reference chain:com.demo.model.Person["createTime"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1300)
at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4568)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3821)
at com.demo.test.JacksonTest.main(JacksonTest.java:27)
解决方案:
- 只需集成对应版本的
jackson-datatype-jsr310
依赖即可:
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.13.5</version><scope>compile</scope></dependency>
1.6 统一配置
我们在进行项目开发的时候,如果每次新增 HTTP 请求接口都要去 VO 里面增加一遍注解的话非常费时费力,我们可以在 SpringBoot 项目中增加一个 Jackson 库的统一配置类:
JacksonConfig.java
importcom.fasterxml.jackson.annotation.JsonInclude;importcom.fasterxml.jackson.databind.DeserializationFeature;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.fasterxml.jackson.databind.module.SimpleModule;importcom.fasterxml.jackson.databind.ser.std.ToStringSerializer;importcom.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;importcom.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;importorg.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.Primary;importorg.springframework.http.converter.json.Jackson2ObjectMapperBuilder;importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;@ConfigurationpublicclassJacksonConfig{@Bean@Primary@ConditionalOnMissingBean(ObjectMapper.class)publicObjectMapperjacksonObjectMapper(Jackson2ObjectMapperBuilder builder){ObjectMapper objectMapper = builder.createXmlMapper(false).build();// 全局配置序修改列化返回 Json 处理方案
objectMapper.registerModule(newSimpleModule()// Json Long --> String,防止精度丢失.addSerializer(Long.class,ToStringSerializer.instance)// 自定义序列化时 LocalDateTime 时间日期格式.addSerializer(LocalDateTime.class,newLocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))// 自定义反序列化时 LocalDateTime 时间日期格式.addDeserializer(LocalDateTime.class,newLocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));// 在序列化时,忽略值为 null 的属性
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);// 在序列化是,忽略值为默认值的属性(例如 int 的原始类型会有默认值)
objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);// 在反序列化时,忽略在 json 中存在但 Java 对象不存在的属性。
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);return objectMapper;}}
修改 Person.java,去除注解:
publicclassPerson{...// 日期测试privateLocalDateTime createTime;}
请求接口如下:
@GetMapping("/person")@ResponseBodypublicResult<Object>person(){Person person =newPerson();
person.setCreateTime(LocalDateTime.now());returnResult.succeed().setData(person);}
请求结果:
可以看到全局配置正常生效了。更多配置信息可以查看 Jackson 库中的
DeserializationFeature
、
SerializationFeature
和
Include
源码内容。
1.7 常用注解
Jackson 库支持使用注解调整它的序列化和序列化机制。常见的注解及用法如下:
@JsonPropertyOrder
:用于类,指定属性在序列化时 json 中的顺序。@JsonIgnoreProperties
:用于类,批量忽视属性,不进行序列化。@JsonNaming
:用于类,在序列化与反序列化时的驼峰命名、小写字母命名转换。@JsonIgnore
:用于字段,忽略属性,不进行序列化。@JsonProperty
:用于字段,定制序列化的变量名。@JsonFormat
:用于字段,指定时间日期字段的格式。@JsonSerialize
:用于字段,指定字段的序列化方式。@JsonDeserialize
:用于字段,指定字段的反序列化方式。@JsonCreator
:用于构造函数,配合 @JsonProperty 使用,用于定制反序列化机制。@JsonAnyGetter
:用于修饰方法,负责序列化时处理 JSON 对象中未知的键值对。@JsonAnySetter
:用于修饰方法,负责反序列化时处理 JSON 对象中未知的键值对。
使用示例:
// 用于类,指定属性在序列化时 json 中的顺序@JsonPropertyOrder({"date","user_name"})// 批量忽略属性,不进行序列化@JsonIgnoreProperties(value ={"other"})// 用于序列化与反序列化时的驼峰命名与小写字母命名转换@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)publicstaticclassUser{@JsonIgnoreprivateMap<String,Object> other =newHashMap<>();// 正常case@JsonProperty("user_name")privateString userName;// 空对象caseprivateInteger age;@JsonFormat(timezone ="GMT+8", pattern ="yyyy-MM-dd HH:mm:ss")// 日期转换caseprivateDate date;// 默认值caseprivateint height;publicUser(){}// 反序列化执行构造方法@JsonCreatorpublicUser(@JsonProperty("user_name")String userName){System.out.println("@JsonCreator 注解使得反序列化自动执行该构造方法 "+ userName);// 反序列化需要手动赋值this.userName = userName;}@JsonAnySetterpublicvoidset(String key,Object value){
other.put(key, value);}@JsonAnyGetterpublicMap<String,Object>any(){return other;}// 本文默认省略getter、setter方法}
测试代码:
publicstaticvoidmain(String[] args)throwsIOException{ObjectMapper mapper =newObjectMapper();// 造数据Map<String,Object> map =newHashMap<>();
map.put("user_name","Tom");
map.put("date","2020-07-26 19:28:44");
map.put("age",100);
map.put("demoKey","demoValue");String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);System.out.println(jsonString);System.out.println("反序列化");User user = mapper.readValue(jsonString,User.class);System.out.println(user);System.out.println("序列化");
jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);System.out.println(jsonString);}
执行结果:
1.8 自定义序列化和反序列化
当 Jackson 库默认序列化和反序列化的类不能满足实际需要,可以自定义新的序列化和反序列化的类。
自定义序列化类
:自定义的序列化类需要直接或间接集成 StdSerializer 或 JsonSerializer,同时需要利用 JsonGenerator 生成 json,重写方法 serialize(),示例如下:
publicstaticclassCustomSerializerextendsStdSerializer<Person>{protectedCustomSerializer(){super(Person.class);}@Overridepublicvoidserialize(Person person,JsonGenerator jgen,SerializerProvider provider)throwsIOException{
jgen.writeStartObject();
jgen.writeNumberField("age", person.getAge());
jgen.writeStringField("name", person.getName());
jgen.writeStringField("msg","已被自定义序列化");
jgen.writeEndObject();}}
JsonGenerator 有多种 write 方法以支持生成复杂类型的 json,比如 writeArray()、writeTree() 等。若想单独创建 JsonGenerator,可以通过 JsonFactory() 的 createGenerator()。
自定义反序列化类
:自定义的反序列化类需要直接或间接集成 StdDeserializer 或 StdDeserializer,同时需要利用 JsonParser 读取 json,重写方法 deserilize(),示例如下:
publicstaticclassCustomDeserializerextendsStdDeserializer<Person>{protectedCustomDeserializer(){super(Person.class);}@OverridepublicPersondeserialize(JsonParser jp,DeserializationContext ctxt)throwsIOException,JsonProcessingException{JsonNode node = jp.getCodec().readTree(jp);Person person =newPerson();int age =(Integer)((IntNode) node.get("age")).numberValue();String name = node.get("name").asText();
person.setAge(age);
person.setName(name);return person;}}
JsonParser 提供很多方法来读取 json 信息,如 isClosed()、nextToken()、getValueAsString() 等。若想单独创建 JsonParser,可以通过 JsonFactory() 的 createParser()。
测试示例:
创建好自定义序列化类和自定义反序列化类,若想在程序中调用它们,还需要注册到 ObjectMapper 的 Module,示例如下:
@Testpublicvoidtest9()throwsIOException{ObjectMapper mapper =newObjectMapper();// 生成 moduleSimpleModulemodule=newSimpleModule("myModule");module.addSerializer(newCustomSerializer());module.addDeserializer(Person.class,newCustomDeserializer());// 注册 module
mapper.registerModule(module);// 造数据Person person =newPerson();
person.setName("Tom");
person.setAge(40);
person.setDate(newDate());System.out.println("序列化");String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);System.out.println(jsonString);System.out.println("反序列化");Person deserializedPerson = mapper.readValue(jsonString,Person.class);System.out.println(deserializedPerson);}
或者也可以通过注解方式加在 java 对象的属性、方法或类上来调用它们:
- @JsonSerialize(using = CustomSerializer.class)
- @JsonDeserialize(using = CustomDeserializer.class)
1.9 Jackson 工具类
publicclassJacsonUtils{publicstaticfinalObjectMapperMAPPER;static{MAPPER=newObjectMapper();MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);MAPPER.setDateFormat(newSimpleDateFormat("yyyy-MM-dd HH:mm:ss"));}//===============================序列化================//@SneakyThrowspublicstaticStringtoString(Object obj){if(obj ==null)returnnull;if(obj.getClass()==String.class)returnConvert.toStr(obj);returnMAPPER.writeValueAsString(obj);}//==========================反序列化===============////对象反序列化@SneakyThrows(value={IOException.class})publicstatic<T>TtoBean(String json,Class<T> tClass){if(StrUtil.isEmpty(json))returnnull;returnMAPPER.readValue(json, tClass);}//集合反序列化@SneakyThrows(value={IOException.class})publicstatic<E>List<E>toList(String json,Class<E> eClass){if(StrUtil.isEmpty(json))returnnull;JavaType javaType =MAPPER.getTypeFactory().constructParametricType(List.class,Bean.class);returnMAPPER.readValue(json,javaType);}//Map集合反序列化@SneakyThrows(value={IOException.class})publicstatic<K,V>Map<K,V>toMap(String json,Class<K> kClass,Class<V> vClass){if(StrUtil.isEmpty(json))returnnull;JavaType javaType =MAPPER.getTypeFactory().constructParametricType(HashMap.class,String.class,Bean.class);returnMAPPER.readValue(json, javaType);}//复杂对象反序列化@SneakyThrows(value={IOException.class})publicstatic<T>TnativeRead(String json,TypeReference<T> type){if(StrUtil.isEmpty(json))returnnull;returnMAPPER.readValue(json, type);}}
二、FastJSON 方案
FastJSON
是一个高性能、功能全面的 Java JSON 处理库,由 阿里巴巴 集团开发并开源。其核心功能在于高效地处理 JSON 数据与 Java 对象之间的相互转换,同时也提供了丰富的 JSON 解析、生成与操作功能。
2.1 FastJSON 的特点
- 高性能: FastJSON 以其卓越的性能著称,在处理大量 JSON 数据时表现出较高的效率。通过优化算法和底层实现,它在速度和内存占用上相比许多其它 JSON 库具有竞争优势,特别适合于高并发、大数据量的场景。
- 广泛的功能支持: 序列化与非序列化、泛型与复杂类型处理、日期与时间格式化、注解驱动配置、防止JSON注入等。
2.2 FastJSON 的核心类
FastJSON 库中我们常用的核心类有以下4个:
JSON
:是 FastJSON 处理 json 的入口,相当于一个工具类,提供了以下方法来进行序列化和反序列化:序列化方法:- toJSONString(Object object): 将给定的 Java 对象序列化为 JSON 文本字符串。- toJSONBytes(Object object, Charset charset): 将给定的 Java 对象序列化为 JSON 字节数组,使用指定的字符集(例:StandardCharsets.UTF_8)。反序列化方法:- parseObject(String text, Class<T> clazz): 将给定的 JSON 文本字符串反序列化为指定的 Java 对象。- parseArray(String text, Class<T> clazz): 将给定的 JSON 数组文本字符串反序列化为指定元素类型的 Java 对象。JSONObject
/JSONArray
:解析后的对象或数组。JSONPath
:采用 path 方式获取 json 值。JSONReader
:json 的读取器。
2.2 Maven依赖
<!-- FastJSON --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency>
2.3 代码示例
User.java
importcom.alibaba.fastjson.annotation.JSONField;importlombok.AllArgsConstructor;importlombok.Data;importjava.time.LocalDateTime;/**
* 一个简单的 Java Bean 类
*/@Data@AllArgsConstructorpublicclassUser{privateint id;privateString name;@JSONField(format ="yyyy-MM-dd HH:mm:ss")privateLocalDateTime birthday;privateAddress address;publicUser(int id,String name){this.id = id;this.name = name;}@Data@AllArgsConstructorpublicstaticclassAddress{privateString street;privateString city;}}
测试代码:
publicstaticvoidmain(String[] args){// 序列化单个对象User user =newUser(1,"ACGkaka",LocalDateTime.now(),newUser.Address("Street1","City1"));String jsonString =JSON.toJSONString(user);System.out.println("Serialized JSON: "+ jsonString);// 反序列化单个对象User deserializedUser =JSON.parseObject(jsonString,User.class);System.out.println("Deserialized User: "+ deserializedUser);// 序列化单个对象,保留null值和去掉空格String formattedJson =JSON.toJSONString(user,SerializerFeature.WriteNullListAsEmpty,SerializerFeature.WriteMapNullValue,SerializerFeature.PrettyFormat);System.out.println("Formatted JSON with null values preserved: \n"+ formattedJson);// 序列化多个对象List<User> userList =Arrays.asList(newUser(1,"Alice"),newUser(2,"Bob"));String usersJson =JSON.toJSONString(userList);System.out.println("Users as JSON: "+ usersJson);// 反序列化多个对象List<User> users =JSON.parseArray(usersJson,User.class);System.out.println("Parsed Users from JSON: "+ users);}
执行结果:
2.3 统一配置
一般场景下不需要配置类,采用默认的方式即可。如需配置类进行特殊配置,可以参考如下代码:
@ConfigurationpublicclassFJsonConfig{@BeanpublicHttpMessageConverterconfigureMessageConverters(){FastJsonHttpMessageConverter converter =newFastJsonHttpMessageConverter();FastJsonConfig config =newFastJsonConfig();
converter.setFastJsonConfig(config);List<MediaType> mediaTypeList =newArrayList<>();
mediaTypeList.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypeList);return converter;}}
2.4 常用注解
@JSONField
:FastJSON 的核心注解,基本所有的序列化和反序列化操作都是通过这个注解实现的。注解的属性如下:
- name: 设置序列后别名。
- format: 格式化后输出日期。
- ordinal: 输出排列顺序。
- serialize: 是否序列化输出。
- deserialize: 是否反序列化载入。
2.5 SpringBoot 设置 FastJSON 为默认Json解析框架
如果我们基于某些特殊的场景,必须要使用 FastJSON 作为 SpringBoot 默认的 Json 解析框架,首先要排除 jackson 依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 排除jackson依赖 --><exclusions><exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></exclusion></exclusions></dependency>
然后,创建配置类如下所示:
DefaultJsonConfig.java
importcom.alibaba.fastjson.serializer.SerializerFeature;importcom.alibaba.fastjson.support.config.FastJsonConfig;importcom.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;importorg.springframework.boot.autoconfigure.http.HttpMessageConverters;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.MediaType;importorg.springframework.http.converter.StringHttpMessageConverter;importjava.nio.charset.StandardCharsets;importjava.util.ArrayList;importjava.util.List;/**
* 默认JSON框架配置类
*/@ConfigurationpublicclassDefaultJsonConfig{/**
* 利用 fastJSON 替换掉 jackson,
*/@BeanpublicHttpMessageConvertersfastJsonHttpMessageConverters(){// 配置StringHttpMessageConverter,解决返回中文乱码问题StringHttpMessageConverter stringHttpMessageConverter =newStringHttpMessageConverter(StandardCharsets.UTF_8);List<MediaType> supportedMediaTypes =newArrayList<>();
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_HTML);
stringHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
stringHttpMessageConverter.setWriteAcceptCharset(false);// 配置fastjsonFastJsonHttpMessageConverter fastConverter =newFastJsonHttpMessageConverter();FastJsonConfig fastJsonConfig =newFastJsonConfig();
config.setSerializerFeatures(// 可以配置不同的属性序列化方式SerializerFeature.WriteMapNullValue,// SerializerFeature.WriteNullStringAsEmpty,// SerializerFeature.WriteNullNumberAsZero,// SerializerFeature.WriteNullListAsEmpty,// SerializerFeature.WriteNullBooleanAsFalse,SerializerFeature.DisableCircularReferenceDetect// 格式化// SerializerFeature.PrettyFormat);
converter.setDefaultCharset(Charset.forName("UTF-8"));// 设置支持的MediaTypeList<MediaType> fastMediaTypes =newArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON);
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
fastConverter.setFastJsonConfig(fastJsonConfig);returnnewHttpMessageConverters(fastConverter, stringHttpMessageConverter);}}
三、GSON 方案
- GitHub: https://github.com/google/gson
GSON
是 Google 开发的一款强大的 Java 库,主要用于处理 JSON 数据与 Java 对象之间的序列化与反序列化。GSON 通过将 JSON 数据结构与 Java 对象模型相互转换,简化了 Java 应用中 JSON 数据的处理过程,广泛应用于 Web 服务、移动应用、数据交换等领域。
3.1 GSON 的特点
- 支持序列化、反序列化。
- 支持泛型。
- 支持 JSON 解析器(JsonParser)与生成器(JsonWriter),允许直接读写 JSON 流,适用于需要更多控制权或处理大型 JSON 文档的场景。
3.2 Maven依赖
<!-- GSON --><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.10.1</version></dependency>
3.3 代码示例
User.java
importcom.google.gson.annotations.SerializedName;importlombok.AllArgsConstructor;importlombok.Data;importjava.time.LocalDateTime;/**
* 一个简单的 Java Bean 类
*/@Data@AllArgsConstructorpublicclassUser{privateint id;@SerializedName("name")privateString name;privateLocalDateTime birthday;privateAddress address;publicUser(int id,String name){this.id = id;this.name = name;}@Data@AllArgsConstructorpublicstaticclassAddress{privateString street;privateString city;}}
测试代码:
publicstaticvoidmain(String[] args){// 序列化单个对象User user =newUser(1,"路人甲",LocalDateTime.now(),newUser.Address("Street1","City1"));JsonSerializer<LocalDateTime> localDateTimeSerializer =(localDateTime, type, jsonSerializationContext)->newJsonPrimitive(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));JsonDeserializer<LocalDateTime> localDateTimeDeserializer =(json, typeOfT, context)->LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));Gson gson =newGsonBuilder()// 设置 LocalDateTime 序列化格式.registerTypeAdapter(LocalDateTime.class, localDateTimeSerializer)// 设置 LocalDateTime 反序列化格式.registerTypeAdapter(LocalDateTime.class, localDateTimeDeserializer).serializeNulls().create();String jsonString = gson.toJson(user);System.out.println("Serialized JSON: "+ jsonString);// 反序列化单个对象User deserializedUser = gson.fromJson(jsonString,User.class);System.out.println("Deserialized User: "+ deserializedUser);// 序列化单个对象,保留null值和去掉空格(GSON默认保留null值,格式化可使用GsonBuilder)Gson prettyGson =newGsonBuilder().registerTypeAdapter(LocalDateTime.class, localDateTimeSerializer).registerTypeAdapter(LocalDateTime.class, localDateTimeDeserializer).setPrettyPrinting().create();String formattedJson = prettyGson.toJson(user);System.out.println("Formatted JSON with null values preserved:\n"+ formattedJson);// 序列化多个对象List<User> userList =Arrays.asList(newUser(2,"路人乙"),newUser(3,"路人丙"));String usersJson = gson.toJson(userList);System.out.println("Users as JSON: "+ usersJson);// 反序列化多个对象Type userListType =newTypeToken<List<User>>(){}.getType();List<User> users = gson.fromJson(usersJson, userListType);System.out.println("Parsed Users from JSON: "+ users);}
执行结果:
3.4 SpringBoot 设置 GSON 为默认Json解析框架
如果我们基于某些特殊的场景,必须要使用 FastJSON 作为 SpringBoot 默认的 Json 解析框架,首先要排除 jackson 依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 排除jackson依赖 --><exclusions><exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></exclusion></exclusions></dependency>
然后,创建配置类如下所示:
DefaultJsonConfig.java
importcom.google.gson.*;importorg.springframework.boot.autoconfigure.http.HttpMessageConverters;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.MediaType;importorg.springframework.http.converter.StringHttpMessageConverter;importorg.springframework.http.converter.json.GsonHttpMessageConverter;importjava.nio.charset.StandardCharsets;importjava.time.LocalDate;importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;importjava.util.ArrayList;importjava.util.List;/**
* 默认JSON框架配置类
*/@ConfigurationpublicclassDefaultJsonConfig{/**
* GSON时间日期-序列化机制
*/privatefinalstaticJsonSerializer<LocalDateTime> jsonSerializerDateTime =(localDateTime, type, jsonSerializationContext)->newJsonPrimitive(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));privatefinalstaticJsonSerializer<LocalDate> jsonSerializerDate =(localDate, type, jsonSerializationContext)->newJsonPrimitive(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));/**
* GSON时间日期-反序列化机制
*/privatefinalstaticJsonDeserializer<LocalDateTime> jsonDeserializerDateTime =(jsonElement, type, jsonDeserializationContext)->LocalDateTime.parse(jsonElement.getAsJsonPrimitive().getAsString(),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));privatefinalstaticJsonDeserializer<LocalDate> jsonDeserializerDate =(jsonElement, type, jsonDeserializationContext)->LocalDate.parse(jsonElement.getAsJsonPrimitive().getAsString(),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));/**
* 利用 GSON 替换掉 jackson,
*/@BeanpublicHttpMessageConvertersgsonHttpMessageConverters(){// 配置StringHttpMessageConverter,解决返回中文乱码问题StringHttpMessageConverter stringHttpMessageConverter =newStringHttpMessageConverter(StandardCharsets.UTF_8);List<MediaType> supportedMediaTypes =newArrayList<>();
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_HTML);
stringHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
stringHttpMessageConverter.setWriteAcceptCharset(false);// 配置gsonGson gson =newGsonBuilder()// 设置 LocalDateTime 序列化、反序列化格式.registerTypeAdapter(LocalDateTime.class, jsonSerializerDateTime).registerTypeAdapter(LocalDateTime.class, jsonDeserializerDateTime)// 设置 LocalDate 序列化、反序列化格式.registerTypeAdapter(LocalDate.class, jsonSerializerDate).registerTypeAdapter(LocalDate.class, jsonDeserializerDate)// 序列化Null值// .serializeNulls().create();GsonHttpMessageConverter gsonConverter =newGsonHttpMessageConverter();
gsonConverter.setGson(gson);// 设置支持的MediaTypeList<MediaType> fastMediaTypes =newArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON);
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
gsonConverter.setSupportedMediaTypes(fastMediaTypes);
gsonConverter.setDefaultCharset(StandardCharsets.UTF_8);returnnewHttpMessageConverters(gsonConverter, stringHttpMessageConverter);}}
整理完毕,完结撒花~🌻
参考地址:
1.SpringBoot使用Json序列化(jackson-fastjson-gson等),https://blog.csdn.net/QingChunBuSanChang/article/details/125370687
2.Jackson、gson官方文档以及下载地址,https://blog.csdn.net/banzhengyu/article/details/131168023
3.Jackson入门,https://www.cnblogs.com/mjoe/p/14930842.html
4.Spring Boot整合系列——Gson完整详细版,https://blog.csdn.net/Saykuray/article/details/110506500
5.Gson使用中LocalDateTime和String转化,https://blog.csdn.net/weixin_39406978/article/details/110929874
版权归原作者 不愿放下技术的小赵 所有, 如有侵权,请联系我们删除。