使用Jackson进行序列化和反序列化


前言

  Java生态系统中有很多处理JSON数据的库,比如Gsonfastjson等。关于Jackson相对于其他的序列化类库的区别和优势,网上有很多非常详细的文章。只提一点就是JacksonSpringSpringBoot以及一些远程调用框架例如Feign中默认使用的序列化类库。所以在日常的web项目开发中可以直接使用Jackson,而不用额外引入其它的序列化类库。

  本篇文章主要针对如何在日常项目开发中使用Jackson,以及在使用Jackson的时候需要注意的问题。


一、Jackson的引入

如果是web项目,则已经包含了Jackson的依赖,无需再引用。如果没有则需要在pom文件中引入依赖

<dependencies>
  <!-- Jackson core -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>version</version>
  </dependency>
  <!-- Jackson databind -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>version</version>
  </dependency>
  <!-- Jackson annotations -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>version</version>
  </dependency>
</dependencies>

二、自定义配置Jackson

1.通过@Configuration注解配置

直接引入这篇文章的配置内容:Jackson配置类

@Configuration
public class JacksonConfig {

    @Bean("objectMapper")
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper getObjectMapper(Jackson2ObjectMapperBuilder builder) {

        ObjectMapper mapper = builder.build();

        // 日期格式
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

        //GMT+8
        //map.put("CTT", "Asia/Shanghai");
        mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));

        // Include.NON_NULL 属性为NULL 不序列化
        //ALWAYS // 默认策略,任何情况都执行序列化
        //NON_EMPTY // null、集合数组等没有内容、空字符串等,都不会被序列化
        //NON_DEFAULT // 如果字段是默认值,就不会被序列化
        //NON_ABSENT // null的不会序列化,但如果类型是AtomicReference,依然会被序列化
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        //允许字段名没有引号(可以进一步减小json体积):
        mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

        //允许单引号:
        mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

        // 允许出现特殊字符和转义符
        //mapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);这个已经过时。
        mapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);

        //允许C和C++样式注释:
        mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);

        //序列化结果格式化,美化输出
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        //枚举输出成字符串
        //WRITE_ENUMS_USING_INDEX:输出索引
        mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);

        //空对象不要抛出异常:
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

        //Date、Calendar等序列化为时间格式的字符串(如果不执行以下设置,就会序列化成时间戳格式):
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        //反序列化时,遇到未知属性不要抛出异常:
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        //反序列化时,遇到忽略属性不要抛出异常:
        mapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);

        //反序列化时,空字符串对于的实例属性为null:
        mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

        return mapper;
    }
}

这里面配置和注释已经非常详细了,这里指出几个特别的配置

1.1 SerializationInclusion

序列化的时候如果对象中存在null值该怎么处理,默认情况是ALWAYS,对象的值为null的时候,序列化之后仍保留字段,如果设置成NON_NULL,则为null值的字段序列化成Json字符串的时候会丢失这个字段

1.2 mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)

反序列化的时候遇到未知属性不要抛出异常。如果不设置该属性,则Json字符串里面如果存在实例中不存在的字段,则序列化的时候会报错,这个不大符合咱们web项目的前后端报文交互的实际情况

1.3 ObjectMapper是一个线程安全的单例

这里配置ObjectMapper之后,如果再想使用ObjectMapper进行序列化和反序列化则可通过注入的方式去使用,如果方法中再次创建一个新的ObjectMapper对象则这些配置都将失效

1.4 反序列化的Bean必须有无参构造函数

反序列化的时候默认使用无参构造函数,如果确实没有无参构造函数,则可以使用@Creator来指定构造函数

三、Jackson一些方便的注解

  1. @JsonProperty:该注解用于指定 Java 属性与 JSON 属性之间的映射关系
  2. @JsonIgnore:该注解用于禁用 Java 属性的序列化和反序列化
  3. @JsonFormat:该注解用于指定 Java 属性的日期和时间格式
  4. @JsonInclude:该注解用于指定序列化 Java 对象时包含哪些属性,加在实体类上,相当于全局配置中的ObjectMapper#setSerializationInclusion配置

四、Jackson的使用实战

4.1 Jackson处理嵌套对象、泛型

Jackson可以处理泛型类型,例如List<T>Map<String, T>。在反序列化时,你需要使用TypeReference来指定泛型类型。例如:

String jsonString = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";

ObjectMapper objectMapper = new ObjectMapper();
List<Person> persons = objectMapper.readValue(jsonString, new TypeReference<List<Person>>() {});

同理,嵌套对象也是如此。

4.2 反序列化LocalDateTime等java8的时间类型

4.2.1 在字段上面加@JsonFormat注解

这个方法可以解决,但是每个类的字段上需要添加注解,不是全局配置

4.2.2 添加JavaTimeModule

这个是全局配置,需要添加maven依赖然后再ObjectMapper配置中添加JavaTimeModule模块,因为我在使用的时候配置未生效,所以这里等解决后再进行补充。有懂得大神可以给点提点

4.3 ObjectMapper是否应该保持单例

因为ObjectMapper是线程安全的类,所以使用单例模式可以避免重复的创建和回收对象。

在日常开发使用ObjectMapper的时候就不宜每次都new一个对象了。

4.3.1 使用@Autowirte对象注入的方式或者单例工具类

ObjectMapper在高并发的情况下会存在锁竞争的情况,所以推荐是每个线程持有要给对象,或者是同类功能的类持有一个ObjectMapper对象

private static final ThreadLocal<ObjectMapper> om = new ThreadLocal<ObjectMapper>() {
    @Override
    protected ObjectMapper initialValue() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
};

public static ObjectMapper getObjectMapper() {
    return om.get();
}

具体可以看一下这个stackoverflow:我应该将jackson的objectmapper申明为静态字段嘛? 

总结

推荐在项目中使用Jackson

相关推荐

  1. 使用Jackson进行序列序列

    2024-04-01 13:46:02       17 阅读
  2. Springboot Jackson 序列序列配置

    2024-04-01 13:46:02       33 阅读
  3. Unity-序列序列

    2024-04-01 13:46:02       42 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-01 13:46:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-01 13:46:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-01 13:46:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-01 13:46:02       18 阅读

热门阅读

  1. Android笔记--MediaCodec(一)

    2024-04-01 13:46:02       13 阅读
  2. 英国生物数据库的申请流程

    2024-04-01 13:46:02       13 阅读
  3. flask+uwsgi+云服务器 部署服务端

    2024-04-01 13:46:02       22 阅读
  4. 【微服务篇】分布式事务方案以及原理详解

    2024-04-01 13:46:02       16 阅读
  5. 多线程(24)Future接口

    2024-04-01 13:46:02       15 阅读