本文从本人博客搬运,原文格式更加美观,可以移步原文阅读:Jackson实战
Jackson
是JavaEE项目中最常用的json序列化与反序列化工具,也是SpringBoot
官方推荐的。本文介绍其在实际开发中的各种用法
环境准备
引入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.10</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.10</version>
</dependency>
创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private Integer age;
private Date birth;
}
具体使用
1.基本序列化与反序列化
在Jackson中,序列化和反序列化需要借助ObjectMapper
@Test
public void testSerialize() throws Exception {
// 序列化
User user = new User("baobao", 18,new Date());
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
// 反序列化
User parsedUser = objectMapper.readValue(json, User.class);
System.out.println(parsedUser);
}
{"name":"baobao","age":18,"birth":1615544522878}
User(name=baobao, age=18, birth=Fri Mar 12 18:22:02 CST 2021)
2.指定json字符串的字段名称
默认情况下,java对象转换为json字符串时,对应json字段名称与java对象的属性名称相同。我们也可以通过@JsonProperty
自定义设置
再次运行方法结果如下
{"age":18,"birth":1615544674133,"userName":"baobao"}
3.日期格式化
默认情况下日期会序列化成时间戳,我们可以通过@JsonFormat
指定格式
此时序列化后的结果如下:
{"age":18,"birth":"2021-03-15 01:48:58","userName":"baobao"}
我们发现虽然序列化成了指定的日期格式,但是时间和北京时间查了8个小时,那是因为我们没有指定时区
指定时区后序列化的时间就正确了
{"age":18,"birth":"2021-03-15 09:52:05","userName":"baobao"}
4.序列化数组
@Test
public void testArray() throws Exception {
User user1 = new User("baobao1", 18,new Date());
User user2 = new User("baobao2", 18,new Date());
User user3 = new User("baobao3", 18,new Date());
User[] userArray = {user1, user2, user3};
// 序列化
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(userArray);
System.out.println(json);
// 反序列化
User[] users = objectMapper.readValue(json, User[].class);
for (User user : users) {
System.out.println(user);
}
}
输出结果如下
[{"age":18,"birth":"2021-03-15 09:57:33","userName":"baobao1"},{"age":18,"birth":"2021-03-15 09:57:33","userName":"baobao2"},{"age":18,"birth":"2021-03-15 09:57:33","userName":"baobao3"}]
User(name=baobao1, age=18, birth=Mon Mar 15 09:57:33 CST 2021)
User(name=baobao2, age=18, birth=Mon Mar 15 09:57:33 CST 2021)
User(name=baobao3, age=18, birth=Mon Mar 15 09:57:33 CST 2021)
5.序列化集合
@Test
public void testList() throws Exception {
User user1 = new User("baobao1", 18,new Date());
User user2 = new User("baobao2", 18,new Date());
User user3 = new User("baobao3", 18,new Date());
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
ObjectMapper objectMapper = new ObjectMapper();
// 序列化
String json = objectMapper.writeValueAsString(userList);
System.out.println(json);
// 反序列化
// 方式一:反序列化后不带泛型
List list = objectMapper.readValue(json, List.class);
for (Object o : list) {
System.out.println(o);
}
// 方式二:序列化后携带泛型,方便使用
List<User> users = objectMapper.readValue(json, new TypeReference<List<User>>() {});
for (User user : users) {
System.out.println(user);
}
}
运行结果如下
[{"age":18,"birth":"2021-03-15 10:05:26","userName":"baobao1"},{"age":18,"birth":"2021-03-15 10:05:26","userName":"baobao2"},{"age":18,"birth":"2021-03-15 10:05:26","userName":"baobao3"}]
{age=18, birth=2021-03-15 10:05:26, userName=baobao1}
{age=18, birth=2021-03-15 10:05:26, userName=baobao2}
{age=18, birth=2021-03-15 10:05:26, userName=baobao3}
User(name=baobao1, age=18, birth=Mon Mar 15 10:05:26 CST 2021)
User(name=baobao2, age=18, birth=Mon Mar 15 10:05:26 CST 2021)
User(name=baobao3, age=18, birth=Mon Mar 15 10:05:26 CST 2021)
6.Map的序列化与反序列化
@Test
public void testMap() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// 序列化Map
Map<String, Object> map = new HashMap<>();
map.put("birth", new Date());
map.put("name", "baobao");
map.put("age", 18);
map.put("address", "zjut");
String json = objectMapper.writeValueAsString(map);
System.out.println(json);
// 反序列化为Map
Map<String, Object> parsedMap = objectMapper.readValue(json, new TypeReference<Map<String, Object>>(){});
System.out.println(parsedMap);
}
运行结果如下
{"address":"zjut","name":"baobao","birth":1615774526123,"age":18}
{address=zjut, name=baobao, birth=1615774526123, age=18}
7.序列化时忽略值为null的属性
默认情况下将一个java对象序列化为json字符串,java对象中值为null的属性也会包含在json字符串中
@Test
public void testNull() throws Exception {
User user = new User("baobao", null, new Date());
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
}
{"age":null,"birth":"2021-03-15 10:22:42","userName":"baobao"}
如果想要忽略值为null的属性,有以下2种方式:
- 在java类上标注
@JsonInclude(JsonInclude.Include.NON_NULL)
- 对
objectMapper
进行设置
用以上任意一种方式设置完成后序列化效果如下
{"birth":"2021-03-15 10:27:17","userName":"baobao"}
8.序列化反序列化时排除属性
可以在java类的属性上标注@JsonIgnore
,或者在类上标注@JsonIgnoreProperties
指定多个属性,这样对应的属性将不参与序列化与反序列化
此时再对User进行序列化和反序列化会忽略birth和userName属性,结果如下
@Test
public void testNull() throws Exception {
User user = new User("baobao", 18, new Date());
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
String json1 = "{\"birth\":\"2021-03-12\",\"age\":18,\"userName\":\"baobao\"}";
User value = objectMapper.readValue(json1, User.class);
System.out.println(value);
}
{"age":18}
User(name=null, age=18, birth=null)
注意:
- 对于某个属性,如果已经指定过
@JsonFormat
、@JsonProperty
等注解,那么再标注@JsonIgnore
将会失效- 对于标注在类上的
@JsonIgnoreProperties
,指定的属性名称要与@JsonProperty
中指定的属性名一致,而非是原始的属性名- 对于相互依赖的情况,会进入到无限序列化的死循环,此时一定要用
@JsonIgnore
给循环依赖属性标注上
9.反序列化时忽略无法识别的字段
默认情况下,反序列化时如果碰到无法识别为对应java类中属性的字段,就会抛出异常。比如下面的例子,我们在json字符串中定义了一个java类中没有的属性birthday,反序列化将抛出异常
@Test
public void testNotRecognizedField() throws Exception {
String json = "{\"birthday\":\"2021-03-12\",\"age\":18,\"userName\":\"baobao\"}";
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(json, User.class);
System.out.println(user);
}
此时有2种解决方案:
- 针对User类标注
@JsonIgnoreProperties
,指定反序列化时忽略无法识别的字段
- 对
ObjectMapper
进行配置
配置完成后反序列化效果如下
User(name=null, age=18, birth=null)
10.属性可视化
默认情况下不是 java 对象的所有的属性都被序列化和反序列化,换言之,不是所有属性都可视化,默认的属性可视化的规则如下:
- 若该属性修饰符是
public
,该属性可序列化和反序列化 - 若属性的修饰符不是
public
,但是它的getter
方法和setter
方法是public
,该属性可序列化和反序列化。因为getter
方法用于序列化, 而setter
方法用于反序列化 - 若属性只有
public
的setter
方法,而无public
的getter
方 法,该属性只能用于反序列化
我们将User对象去掉所有getter
和setter
方法
然后再测试其序列化与反序列化
{"birth":"2021-03-15 11:22:03","userName":"baobao"}
User(name=baobao, age=null, birth=Mon Mar 15 11:22:03 CST 2021)
可以发现:
如果java类中的属性标注了
@JsonProperty
、@JsonFormat
等注解,那么即便它们是private的,也会被成功序列化与反序列化
若想更改默认的属性可视化的规则,需要调用ObjectMapper
的方法setVisibility
。下面的示例使修饰符为private的属性age也可以序列化和反序列化。
结果如下
{"age":18,"birth":"2021-03-15 11:26:35","userName":"baobao"}
User(name=baobao, age=18, birth=Mon Mar 15 11:26:35 CST 2021)
SpringBoot项目中推荐的全局配置
我们利用Jackson进行序列化和反序列化时,都要用到ObjectMapper
。建议将自定义ObjectMapper
在容器中全局只放置一份,在不同的地方使用时只要从容器中取即可,这么做是因为:
ObjectMapper
是线程安全的,这样做方便直接注入使用ObjectMapper
的创建过程是一个重量级操作,比较耗时,应当尽可能重复利用,避免创建新的对象
@Configuration
public class ObjectMapperConfig {
@Bean
@Primary // 添加Primary是因为SpringBoot默认已经在容器中放置了一个ObjectMapper,我们自定义的需要优先覆盖它
public ObjectMapper objectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
// 对于空的对象转json的时候不抛出错误
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 允许属性名称没有引号
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 允许属性名称用单引号
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 反序列化时忽略在json字符串中存在但在java对象实际没有的属性,不抛出异常
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 序列化时结果不包含值为null的属性
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
}
或者也可以不重新创建bean,直接在yaml中给默认注入的ObjectMapper
设置这些属性
spring:
jackson:
serialization:
FAIL_ON_EMPTY_BEANS: false
deserialization:
FAIL_ON_UNKNOWN_PROPERTIES: false
default-property-inclusion: NON_NULL
parser:
ALLOW_UNQUOTED_FIELD_NAMES: true
ALLOW_SINGLE_QUOTES: true
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8