对于每个开发web的人来说,JSON肯定都不陌生.它的轻量级与简洁性,使得它备受青睐.同时,随着前后台技术的不断成熟,Json解析技术也发展地越来越完善.在这众多的Json解析技术中,备受欢迎的则是我们今天要讲的以下三种:
-
Jackson
-
Gson
-
json-b
1.jackson
Jackson是一个简单基于Java应用库,Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象。Jackson所依赖的jar包较少,简单易用并且性能也要相对高些,并且Jackson社区相对比较活跃,更新速度也比较快。从SpringMVC到SpringBoot,官方推荐的都是jackson.,它主要具备以下几个优点:容易使用。
-
容易使用 - jackson API提供了一个高层次外观,以简化常用的用例。
-
无需创建映射 - API提供了默认的映射大部分对象序列化。
-
性能高 - 快速,低内存占用,适合大型对象图表或系统。
-
干净的JSON - jackson创建一个干净和紧凑的JSON结果,这是让人很容易阅读。
-
不依赖 - 库不需要任何其他的库,除了JDK。
-
开源代码 - jackson是开源的,可以免费使用。
1.1 jackson的常规使用
对于我们而言,我们比较关心的是他的序列化操作,这里我们先引入jackson的依赖包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
构建成功后,我们看到依赖里出现了:
jackson-annotations-2.9.0.jar
jackson-core-2.9.7.jar
jackson-databind-2.9.7.jar
这是jackson的依赖库,接下来我们创建一个基本的实体类:
package top.lianmengtu.testjson.domain;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserInfo {
private Long id;
private String nickName;
private Boolean gender;
}
我们来一个测试类:
package top.lianmengtu.testjson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import top.lianmengtu.testjson.domain.UserInfo;
public class MyJsonTest {
public static void main(String[] args) {
ObjectMapper objectMapper=new ObjectMapper();
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setNickName("Nick");
userInfo.setGender(false);
String userInfoString= null;
try {
userInfoString = objectMapper.writeValueAsString(userInfo);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println(userInfoString);
}
}
运行结果如下:
{"id":1,"nickName":"Nick","gender":false}
这是一个基本类型,我们这里添加两个属性,一个基本类型的List,另一个是日期类型:
package top.lianmengtu.testjson.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
private Long id;
private String nickName;
private Boolean gender;
private List<String> roleList;
private Date createTime;
}
测试类如下:
package top.lianmengtu.testjson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class MyJsonTest {
public static void main(String[] args) {
ObjectMapper objectMapper =new ObjectMapper();
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
userInfo.setCreateTime(new Date());
String userInfoString= null;
try {
userInfoString = objectMapper.writeValueAsString(userInfo);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println(userInfoString);
}
}
运行结果如下:
{"id":1,"nickName":"Nick","gender":false,"roleList":["admin","manager"],"createTime":1540451548817}
这里我们看到基本类型的List按着我们的期望解析了,但日期明显不是我们想要的,这里我们可以使用@JsonFormat来传入我们想要的格式,如下所示:
package top.lianmengtu.testjson.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
private Long id;
private String nickName;
private Boolean gender;
private List<String> roleList;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
这里我们的测试类不变,运行结果如下所示:
{"id":1,"nickName":"Nick","gender":false,"roleList":["admin","manager"],"createTime":"2018-10-25 07:16:12"}
此时,我们看到日期类型已经按着我们期望的格式进行展示了.我们在使用的时候,常会使用内嵌类型,这里我们给用户定义一个公司:
package top.lianmengtu.testjson.domain;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Company {
private Long id;
private String name;
private String address;
}
我们再给用户定义一个公司列表,表明用户工作过的公司:
package top.lianmengtu.testjson.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
private Long id;
private String nickName;
private Boolean gender;
private List<String> roleList;
private List<Company> companies;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
我们也修改一下测试类:
package top.lianmengtu.testjson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class MyJsonTest {
public static void main(String[] args) {
ObjectMapper objectMapper =new ObjectMapper();
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
userInfo.setCreateTime(new Date());
String userInfoString= null;
try {
userInfoString = objectMapper.writeValueAsString(userInfo);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println(userInfoString);
}
}
运行结果如下:
{"id":1,"nickName":"Nick","gender":false,"roleList":["admin","manager"],"companies":[{"id":1,"name":"百度","address":"西二旗"}],"createTime":"2018-10-25 07:19:42"}
1.2 SpringBoot中的jackson
展示了基本使用之后,我们现在来看一下SpringBoot对Jackson的支持,当我们在pom中添加了spring-boot-starter-web 之后,我们发现已经自动加入了jackson的三个包.启动类如下:
package top.lianmengtu.testjson;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
JsonController类代码如下所示:
package top.lianmengtu.testjson.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("json/")
public class JsonController {
@RequestMapping("basic_json")
public UserInfo basicJson(){
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
userInfo.setCreateTime(new Date());
return userInfo;
}
}
我们启动项目,然后打开浏览器,结果如下所示:
{"id":1,"nickName":"Nick","gender":false,"roleList":["admin","manager"],"companies":[{"id":1,"name":"百度","address":"西二旗"}],"createTime":"2018-10-25 07:44:13"}
和我们刚刚演示的一样,但现在有两个问题,首先,包含日期类型的model可能不只一个,如果我们在model里添加@JsonFormat可能每一个都需要加,比较地繁琐.其次,有些时候我们可能没有办法改变model的源码,因此不能给诸如日期这样的字段添加@JsonFormat注解,那怎么办呢?此时我们就可以将我们的配置外移,移到application.yml里,如下所示:
spring:
jackson:
date-format: 'yyyy-MM-dd'
然后我们将实体类里的@JsonFormat去掉,如下所示:
package top.lianmengtu.testjson.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
private Long id;
private String nickName;
private Boolean gender;
private List<String> roleList;
private List<Company> companies;
private Date createTime;
}
我们运行我们的代码,发现日期现在变成了我们在配置文件里配置的yyyy-MM-dd的格式:
{"id":1,"nickName":"Nick","gender":false,"roleList":["admin","manager"],"companies":[{"id":1,"name":"百度","address":"西二旗"}],"createTime":"2018-10-25"}
不过这里有一点需要说明一下,如果我们即在yml里进行了配置,同时也使用了@JsonFormat注解,那么@JsonFormat会覆盖yml里的。除此之外,springBoot还支持jackson以下一些属性的使用:
spring.jackson.default-property-inclusion=
spring.jackson.deserialization.*=
spring.jackson.generator.*=
spring.jackson.joda-date-time-format=
spring.jackson.locale=
spring.jackson.mapper.*=
spring.jackson.parser.*=
spring.jackson.property-naming-strategy=
spring.jackson.serialization.*=
spring.jackson.time-zone=
它使用的方式和objectMapper使用的方式几乎一模一样,比如,我们刚刚尝试过关于日期格式的问题,在我们没有写JsonFormat的时候,jackson会自动将我们的日期格式转换为时间戳,现在我们来禁用这个属性
public static void main(String[] args) {
ObjectMapper objectMapper=new ObjectMapper();
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setNickName("Nick");
userInfo.setGender(false);
userInfo.setCreateTime(new Date());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String userInfoString= null;
try {
userInfoString = objectMapper.writeValueAsString(userInfo);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println(userInfoString);
}
因为serialization是一个map,所以在yml里这么写
spring:
jackson:
serialization:
/write_dates_as_timestamps: false
当我们禁用之后,打印出来的字符串如下所示:
{"id":1,"nickName":"Nick","gender":false,"roleList":null,"companies":null,"createTime":"2018-10-25T08:46:32.192+0000"}
我们发现,已经不在是时间戳了,说明我们的配置已经生效了,其他的,大家可以进到相应的实现里去一一查看.
2. gson的使用
gson是google出的一个json解析工具.使用它的时候,我们只需要在pom中添加gson一个包就可以了,如下所示:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
2.1 gson的基本使用
我们还是先来看一下gson的基本使用.我们仍然使用刚刚那个实体类:
package top.lianmengtu.testjson.domain;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
private Long id;
private String nickName;
private Boolean gender;
private List<String> roleList;
private List<Company> companies;
private Date createTime;
}
然后是我们的测试类:
package top.lianmengtu.testjson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class GsonTest {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
userInfo.setCreateTime(new Date());
Gson gson=new Gson();
String userInfoString=gson.toJson(userInfo);
System.out.println(userInfoString);
}
}
然后运行结果如下所示:
{"id":1,"nickName":"Nick","gender":false,"roleList":["admin","manager"],"companies":[{"id":1,"name":"百度","address":"西二旗"}],"createTime":"Oct 25, 2018, 5:12:44 PM"}
现在我们来展示他的日期转换,在gson中我们处理日期转换可以使用gsonbuilder去处理,如下所示(因为重点是日期,所以略过其他字段):
package top.lianmengtu.testjson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.Date;
public class GsonTest {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setCreateTime(new Date());
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("yyyy-MM-dd");
Gson gson=gsonBuilder.create();
String userInfoString=gson.toJson(userInfo);
System.out.println(userInfoString);
}
}
测试结果如下所示:
{"createTime":"2018-10-25"}
gsonbuilder的这种处理方式,除了能处理date类型外,还能处理long类型,可以将long转换为字符串或者转换为数字类型.默认是将long转换为数字,这里我们处理成字符串,测试类如下所示:
package top.lianmengtu.testjson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.LongSerializationPolicy;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.Date;
public class GsonTest {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setCreateTime(new Date());
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("yyyy-MM-dd");
gsonBuilder.setLongSerializationPolicy(LongSerializationPolicy.STRING);
Gson gson=gsonBuilder.create();
String userInfoString=gson.toJson(userInfo);
System.out.println(userInfoString);
}
}
运行结果如下:
{"id":"1","createTime":"2018-10-25"}
之前在json的时候出现过一种情况,就是循环解析导致报错的,最典型的情况是对tree的解析,之前我们曾在tree里设置children来存放子节点,同时又设置了parent来存放父节点,后来就直接报错了.除了这些之外,还有一些纯粹是为了我们的业务逻辑而设置的属性,这些我们也需要忽略,那gson如何实现这个功能呢:
1.字段上标注transient或@Expose
实体类代码如下所示:
package top.lianmengtu.testjson.domain;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
private transient Long id;
private String nickName;
private Boolean gender;
private List<String> roleList;
private List<Company> companies;
private Date createTime;
}
然后我们仍然运行上面的测试代码,运行结果如下所示:
{"createTime":"2018-10-25"}
使用transient是标记需要排除的,而@Expose则标记的是需要序列化的字段.实体类修改如下:
package top.lianmengtu.testjson.domain;
import com.google.gson.annotations.Expose;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
@Expose
private Long id;
@Expose
private String nickName;
@Expose
private Boolean gender;
private List<String> roleList;
private List<Company> companies;
@Expose
private Date createTime;
}
测试代码修改如下:
package top.lianmengtu.testjson;
import com.google.gson.*;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class GsonTest {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setCreateTime(new Date());
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("yyyy-MM-dd");
gsonBuilder.excludeFieldsWithoutExposeAnnotation();
Gson gson=gsonBuilder.create();
String userInfoString=gson.toJson(userInfo);
System.out.println(userInfoString);
}
}
运行结果如下:
{"id":1,"nickName":"Nick","gender":false,"createTime":"2018-10-25"}
2.使用排除策略 exclusionStrategy
排除策略有很多种,另一种是传字符串去匹配,测试代码修改如下:
package top.lianmengtu.testjson;
import com.google.gson.*;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class GsonTest {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setCreateTime(new Date());
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("yyyy-MM-dd");
final List<String> filedList= new ArrayList<>();
filedList.add("companies");
filedList.add("nickName");
gsonBuilder.addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
String name=fieldAttributes.getName();
for(String filed: filedList){
if(filed.equals(name)){
return true;
}
}
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
});
Gson gson=gsonBuilder.create();
String userInfoString=gson.toJson(userInfo);
System.out.println(userInfoString);
}
}
然后我们再运行,得到结果如下:
{"id":1,"gender":false,"roleList":["admin","manager"],"createTime":"2018-10-25"}
2.2 SpringBoot中的GSON
因为SpringBoot是支持SPI的,简单理解就是能抓到哪个,就用哪个实现,因为spring-boot-starter-web默认是用jackson,所以此时为了能够让springboot抓到gson,我们一方面要在pom里加上gson的配置,另一方面还需要将jackson排除掉,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.lianmengtu</groupId>
<artifactId>test-json</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<name>test-json</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<lombok.version>1.18.2</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这样就可以在SpringBoot中启用Gson了.我们可以打开日志查看一下:
GsonAutoConfiguration matched:
- @ConditionalOnClass found required class 'com.google.gson.Gson'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
GsonAutoConfiguration#gson matched:
- @ConditionalOnMissingBean (types: com.google.gson.Gson; SearchStrategy: all) did not find any beans (OnBeanCondition)
GsonAutoConfiguration#gsonBuilder matched:
- @ConditionalOnMissingBean (types: com.google.gson.GsonBuilder; SearchStrategy: all) did not find any beans (OnBeanCondition)
GsonHttpMessageConvertersConfiguration matched:
- @ConditionalOnClass found required class 'com.google.gson.Gson'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
GsonHttpMessageConvertersConfiguration.GsonHttpMessageConverterConfiguration matched:
- AnyNestedCondition 1 matched 1 did not; NestedCondition on GsonHttpMessageConvertersConfiguration.PreferGsonOrJacksonAndJsonbUnavailableCondition.JacksonJsonbUnavailable NoneNestedConditions 0 matched 2 did not; NestedCondition on GsonHttpMessageConvertersConfiguration.JacksonAndJsonbUnavailableCondition.JsonbPreferred @ConditionalOnProperty (spring.http.converters.preferred-json-mapper=jsonb) did not find property 'spring.http.converters.preferred-json-mapper'; NestedCondition on GsonHttpMessageConvertersConfiguration.JacksonAndJsonbUnavailableCondition.JacksonAvailable @ConditionalOnBean (types: org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; SearchStrategy: all) did not find any beans of type org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; NestedCondition on GsonHttpMessageConvertersConfiguration.PreferGsonOrJacksonAndJsonbUnavailableCondition.GsonPreferred @ConditionalOnProperty (spring.http.converters.preferred-json-mapper=gson) did not find property 'spring.http.converters.preferred-json-mapper' (GsonHttpMessageConvertersConfiguration.PreferGsonOrJacksonAndJsonbUnavailableCondition)
- @ConditionalOnBean (types: com.google.gson.Gson; SearchStrategy: all) found bean 'gson' (OnBeanCondition)
现在我们来演示一下日期排除,Controller代码如下:
package top.lianmengtu.testjson.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("gson/")
public class GsonController {
@RequestMapping("basic")
public UserInfo basic(){
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setCreateTime(new Date());
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
return userInfo;
}
}
与jackson配置类似,springboot支持gson,其相关配置在spring.gson下,yml配置修改如下:
spring:
gson:
date-format: 'yyyy-MM-dd HH:mm:ss'
运行后,结果如下:
{"id":1,"nickName":"Nick","gender":false,"roleList":["admin","manager"],"companies":[{"id":1,"name":"百度","address":"西二旗"}],"createTime":"2018-10-25 18:38:38"}
现在我们来演示字段排除,因为transient是自动支持的,所以我们不再演示,我们来演示一下@Expose的使用,配置文件修改如下:
spring:
gson:
date-format: 'yyyy-MM-dd HH:mm:ss'
exclude-fields-without-expose-annotation: true
然后实体类修改如下:
package top.lianmengtu.testjson.domain;
import com.google.gson.annotations.Expose;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class UserInfo {
@Expose
private Long id;
@Expose
private String nickName;
private Boolean gender;
private List<String> roleList;
private List<Company> companies;
private Date createTime;
}
我们在id和nickName字段上加上@Expose,,然后运行代码,效果如下所示:
{"id":1,"nickName":"Nick"}
除了这些之外,它支持的配置如下所示:
spring.gson.date-format=
spring.gson.disable-html-escaping=
spring.gson.disable-inner-class-serialization=
spring.gson.enable-complex-map-key-serialization=
spring.gson.exclude-fields-without-expose-annotation=
spring.gson.field-naming-policy=
spring.gson.generate-non-executable-json=
spring.gson.lenient=
spring.gson.long-serialization-policy=
spring.gson.pretty-printing=
spring.gson.serialize-nulls=
这里有个属性是disable-html-escaping,可以帮助我们过滤html标签,这个在某些场景下还是很有用的
3. JSON-B
json-b是Java EE8中提供的一种json标准.该 API提供了适配器及一些自定义序列化器和反序列化器。对于较高级的场景,可以使用类中的注解或运行时配置构建器来覆盖 JSON-B 的默认设置。
总而言之,JSON-B整理了已在企业开发人员中广泛应用的行业实践和方法。它使用注解通过映射语义来标记类和字段,提供了处理复杂数据结构时常常需要的可扩展性。
最重要的是,这个 API 支持以一种直观且容易的方式在 Java 类与 JSON 文档之间进行绑定。甚至没有 JSON经验的开发人员,应该也能够轻松上手使用 Java API for JSON Binding。对 JSON 反/序列化库(比如 GSON、Boon 和 Jackson)经验丰富的开发人员将发现 JSON-B 很熟悉且用起来很舒适。
我们仍然从两方面来阐述JSON-B的使用,首先是非SpringBoot项目中的使用,其次是SpringBoot项目中的使用
3.1 常规使用
json-b是一种规范,其中Yasson是eclipse实现的,而johnzon-jsonb则是apache实现的.因为json-b是一种规范,所以我们只要加入实现即可,无论哪种实现,使用起来几乎是一样的,我们添加了Yasson,GAV座标如下:
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.2</version>
</dependency>
测试代码如下:
package top.lianmengtu.testjson;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class JSONBTest {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setCreateTime(new Date());
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
Jsonb userJson=JsonbBuilder.create();
String userInfoString=userJson.toJson(userInfo);
System.out.println(userInfoString);
}
}
运行代码后的结果如下:
{"companies":[{"address":"西二旗","id":1,"name":"百度"}],"createTime":"2018-10-25T11:32:17.337Z[UTC]","gender":false,"id":1,"nickName":"Nick","roleList":["admin","manager"]}
首先,我们还是来看日期格式化的问题,测试代码如下:
package top.lianmengtu.testjson;
import top.lianmengtu.testjson.domain.Company;
import top.lianmengtu.testjson.domain.UserInfo;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class JSONBTest {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setId(1l);
userInfo.setCreateTime(new Date());
userInfo.setNickName("Nick");
userInfo.setGender(false);
List<String> roleList=new ArrayList<>();
roleList.add("admin");
roleList.add("manager");
userInfo.setRoleList(roleList);
Company baidu=new Company();
baidu.setId(1l);
baidu.setName("百度");
baidu.setAddress("西二旗");
List<Company> companies = new ArrayList<>();
companies.add(baidu);
userInfo.setCompanies(companies);
JsonbConfig config=new JsonbConfig();
config.withDateFormat("yyyy-MM-dd",Locale.CHINESE);
Jsonb userJson=JsonbBuilder.create(config);
String userInfoString=userJson.toJson(userInfo);
System.out.println(userInfoString);
}
}
运行就结果如下:
{"companies":[{"address":"西二旗","id":1,"name":"百度"}],"createTime":"2018-10-25","gender":false,"id":1,"nickName":"Nick","roleList":["admin","manager"]}
3.2 SpringBoot中的JSON-B
在springBoot中使用JSON-B的方式和GSON相似,我们只需要排除掉jackson然后添加上apache-johnzon的jackson就可以了.这里我们添加的是Apache Johnzon. GAV座标如下:
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>jsonb-api</artifactId>
<version>1.0.0</version>
</dependency>
配置文件如下所示:
JsonbHttpMessageConvertersConfiguration matched:
- @ConditionalOnClass found required class 'javax.json.bind.Jsonb'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
我们看懂这里匹配到的是jsonb,这说明我们的配置已经成功了.controller不变,我们打开浏览器,然后访问localhost:8080/jsonb/basic,结果如下所示:
{"companies":[{"address":"西二旗","id":1,"name":"百度"}],"createTime":"2018-10-25","gender":false,"id":1,"nickName":"Nick","roleList":["admin","manager"]}