侧边栏壁纸
博主头像
包包博主等级

talk is cheap,show me the code

  • 累计撰写 25 篇文章
  • 累计创建 59 个标签
  • 累计收到 30 条评论

Jackson实战

包包
2021-06-25 / 0 评论 / 0 点赞 / 547 阅读 / 8,689 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-19,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

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方法用于反序列化
  • 若属性只有publicsetter方法,而无publicgetter方 法,该属性只能用于反序列化

我们将User对象去掉所有gettersetter方法

然后再测试其序列化与反序列化

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

评论区