JSON 之 Jackson

Jackson 是 Java 生态下的一款 JSON (返)序列化工具,具有高效、强大、安全(没有 Fastjson 那么多的安全漏洞)等特性。同时应用广泛,Spring Boot/Cloud、Akka、Spark 等众多框架都将其作为默认 JSON 处理工具。

依赖

要使用 Jackson,需要在项目中添加如下依赖(注:使用 Spring Boot 时不需要手动添加,Spring 框架已经默认包含):

Maven

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.11.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
    <version>2.11.1</version>
</dependency>

Sbt

libraryDependencies ++= Seq(
  "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.11.1",
  "com.fasterxml.jackson.datatype" % "jackson-datatype-jdk8" % "2.11.1"
)
  • jsr310:Java 8 新加日期、时间类型支持(java.time.*)支持
  • jdk8:Java 8 新加数据类型支持(Optional等)

简明使用

获取 Jackson

Jackson 在使用之前需要实例化一个 ObjectMapper 对像(它不直接提供全局的默认静态方法)。通常我们会将 objectMapper 定义成一个静态成员,或通过 DI 框架注入使用。

Java

public static final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules()

Spring

@Autowired
private ObjectMapper objectMapper;

注:Spring 默认不会自动加载 classpath 路径的所有 Jackson Module。需要在 objectMapper 上调用 registerModule 方法手动注册。

Scala

val objectMapper = new ObjectMapper().findAndRegisterModules()

建议的 Jackson 配置

编码配置

ObjectMapper objectMapper = new ObjectMapper()
            // 自动加载 classpath 中所有 Jackson Module
            .findAndRegisterModules()
            // 时区序列化为 +08:00 形式
            .configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, false)
            // 日期、时间序列化为字符串
            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
            // 持续时间序列化为字符串
            .configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false)
            // 当出现 Java 类中未知的属性时不报错,而是忽略此 JSON 字段
            .configure(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, false)
            // 枚举类型调用 `toString` 方法进行序列化
            .configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true)
            // 设置 java.util.Date 类型序列化格式
            .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
            // 设置 Jackson 使用的时区
            .setTimeZone(SimpleTimeZone.getTimeZone("GMT+8"));

创建 JSON 对像

Jackson 的 ArrayNodeObjectNode 对象均不能直接创建,需要通过 objectMapper 来创建。同时,两个 Node 对像都 JsonNode 的子类。

ArrayNode jsonArray = objectMapper.createArrayNode();
jsonArray.add("Jackson").add("JSON");
ObjectNode jsonObject = objectMapper.createObjectNode()
        .put("title", "Json 之 Jackson")
        .put("readCount", 1024);

反序列化

String jsonText = ....;

// json text -> Jackson json node
JsonNode javaTimeNode = objectMapper.readTree(jsonText);

// json text -> java class
JavaTime javaTime1 = objectMapper.readValue(jsonText, JavaTime.class);

// Jackson json node -> java class
JavaTime javaTime2 = objectMapper.treeToValue(javaTimeNode, JavaTime.class);

序列化

ZonedDateTime zdt = ZonedDateTime.parse(“2020-07-02T14:31:28.822+08:00[Asia/Shanghai]”);
JavaTime javaTime = new JavaTime()
.setLocalDateTime(zdt.toLocalDateTime())
.setZonedDateTime(zdt)
.setOffsetDateTime(zdt.toOffsetDateTime())
.setLocalDate(zdt.toLocalDate())
.setLocalTime(zdt.toLocalTime())
.setDuration(Duration.parse(“P1DT1H1M1.1S”))
.setDate(Date.from(zdt.toInstant()))
.setTimestamp(Timestamp.from(zdt.toInstant()));
out.println(objectMapper.writeValueAsString(javaTime));
out.println(objectMapper.writeValueAsString(jsonObject));
out.println(objectMapper.writeValueAsString(jsonArray));

输出:

{“localDateTime”:“2020-07-02T14:31:28.822”,“zonedDateTime”:“2020-07-02T14:31:28.822+08:00”,“offsetDateTime”:“2020-07-02T14:31:28.822+08:00”,“localDate”:“2020-07-02”,“localTime”:“14:31:28.822”,“duration”:“PT25H1M1.1S”,“date”:“2020-07-02 14:31:28”,“timestamp”:“2020-07-02 14:31:28”}
{“title”:“Json 之 Jackson”,“readCount”:1024}
[“Jackson”,“JSON”]

Java 类转换成 Jackson JsonNode

JsonNode jsonNode = objectMapper.valueToTree(javaTime);

美化输出

objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(javaTime)
{
“localDateTime” : “2020-07-02T14:31:28.822”,
“zonedDateTime” : “2020-07-02T14:31:28.822+08:00”,
“offsetDateTime” : “2020-07-02T14:31:28.822+08:00”,
“localDate” : “2020-07-02”,
“localTime” : “14:31:28.822”,
“duration” : “PT25H1M1.1S”,
“date” : “2020-07-02 14:31:28”,
“timestamp” : “2020-07-02 14:31:28”
}

序列化时忽略某些字段

@JsonIgnore

@JsonIgnore
private Long _version;

@JsonIgnore 注解也可以添加在 getter 函数上。

@JsonIgnoreProperties

@JsonIgnoreProperties({"_version", “timestamp”})
public class JavaTime {
}

自定义序列化、反序列化器

对于某些自定义类型或 Jackson 不支持的类型,可以实现自己的序列化、反序列化器。

POJO

在类型上使用 @JsonSerialize@JsonDeserialize 注解来分别指定序列化和反序列化器。

@Data
public class User {
private String id;
<span class="nd">@JsonSerialize</span><span class="o">(</span><span class="n">using</span> <span class="o">=</span> <span class="n">PgJsonSerializer</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="nd">@JsonDeserialize</span><span class="o">(</span><span class="n">using</span> <span class="o">=</span> <span class="n">PgJsonDeserializer</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">io</span><span class="o">.</span><span class="na">r2dbc</span><span class="o">.</span><span class="na">postgresql</span><span class="o">.</span><span class="na">codec</span><span class="o">.</span><span class="na">Json</span> <span class="n">metadata</span><span class="o">;</span>

}

先将 R2DBC PostgreSQL 的 JSON 转化为 JsonNode 对象,再调用 gen.writeTree 对其进行序列化。这样才能保证序列化出来的字段值是一个 JSON 对象或 JSON 数组。

import io.r2dbc.postgresql.codec.Json;

public class PgJsonSerializer extends StdSerializer<Json> {
public PgJsonSerializer() {
super(Json.class);
}

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">serialize</span><span class="o">(</span><span class="n">Json</span> <span class="n">value</span><span class="o">,</span> <span class="n">JsonGenerator</span> <span class="n">gen</span><span class="o">,</span> <span class="n">SerializerProvider</span> <span class="n">provider</span><span class="o">)</span>
        <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="n">JsonParser</span> <span class="n">parser</span> <span class="o">=</span> <span class="n">gen</span><span class="o">.</span><span class="na">getCodec</span><span class="o">().</span><span class="na">getFactory</span><span class="o">().</span><span class="na">createParser</span><span class="o">(</span><span class="n">value</span><span class="o">.</span><span class="na">asArray</span><span class="o">());</span>
    <span class="n">JsonNode</span> <span class="n">node</span> <span class="o">=</span> <span class="n">gen</span><span class="o">.</span><span class="na">getCodec</span><span class="o">().</span><span class="na">readTree</span><span class="o">(</span><span class="n">parser</span><span class="o">);</span>
    <span class="n">gen</span><span class="o">.</span><span class="na">writeTree</span><span class="o">(</span><span class="n">node</span><span class="o">);</span>
<span class="o">}</span>

}

通过 ObjectCodec#readTreeJsonParse 读取为一个 TreeNode 对像,再将其序列化为 JSON 格式(JSON 字符串的字符数组形式)后传给 Json.of 函数。

import io.r2dbc.postgresql.codec.Json;

public class PgJsonDeserializer extends StdDeserializer<Json> {
public PgJsonDeserializer() {
super(Json.class);
}

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Json</span> <span class="nf">deserialize</span><span class="o">(</span><span class="n">JsonParser</span> <span class="n">p</span><span class="o">,</span> <span class="n">DeserializationContext</span> <span class="n">ctxt</span><span class="o">)</span>
        <span class="kd">throws</span> <span class="n">IOException</span><span class="o">,</span> <span class="n">JsonProcessingException</span> <span class="o">{</span>
    <span class="n">TreeNode</span> <span class="n">node</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">getCodec</span><span class="o">().</span><span class="na">readTree</span><span class="o">(</span><span class="n">p</span><span class="o">);</span>
    <span class="n">ObjectMapper</span> <span class="n">objectMapper</span> <span class="o">=</span> <span class="o">(</span><span class="n">ObjectMapper</span><span class="o">)</span> <span class="n">p</span><span class="o">.</span><span class="na">getCodec</span><span class="o">();</span>
    <span class="kt">byte</span><span class="o">[]</span> <span class="n">value</span> <span class="o">=</span> <span class="n">objectMapper</span><span class="o">.</span><span class="na">writeValueAsBytes</span><span class="o">(</span><span class="n">node</span><span class="o">);</span>
    <span class="k">return</span> <span class="n">Json</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">value</span><span class="o">);</span>
<span class="o">}</span>

}

Jackson Module

当自定义(反)序列化变多时,在每个类上通过注解手工指定(反)序列化器就变得很繁琐。我们可以通过定义一个 Jackson Module 并使用 .findAndRegisterModules() 通过 Java 的 Service 机制自动注册到 ObjectMapper。通过 Module 机制注册了类型的序列化、反序列化器后,不在需要在属性或方法上定义 @JsonSerialize@JsonDeserialize 注解。

1. 定义 SerializersDeserializers

public class ExampleSerializers extends Serializers.Base implements java.io.Serializable {
private static final long serialVersionUID = 1L;

@Override
public JsonSerializer<?> findSerializer(
SerializationConfig config, JavaType type, BeanDescription beanDesc) {
final Class<?> raw = type.getRawClass();
if (Json.class.isAssignableFrom(raw)) {
return new PgJsonSerializer();
}
return super.findSerializer(config, type, beanDesc);
}
}

public class ExampleDeserializers extends Deserializers.Base implements java.io.Serializable {
private static final long serialVersionUID = 1L;

@Override
public JsonDeserializer<?> findBeanDeserializer(
JavaType type, DeserializationConfig config, BeanDescription beanDesc)
throws JsonMappingException {
if (type.hasRawClass(Optional.class)) {
return new PgJsonDeserializer();
}

<span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">findBeanDeserializer</span><span class="o">(</span><span class="n">type</span><span class="o">,</span> <span class="n">config</span><span class="o">,</span> <span class="n">beanDesc</span><span class="o">);</span>

}
}

2. 实现 Module

public class ExampleModule extends com.fasterxml.jackson.databind.Module {
@Override
public void setupModule(SetupContext context) {
context.addSerializers(new ExampleSerializers());
context.addDeserializers(new ExampleDeserializers());
}
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getModuleName</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="s">"ExampleModule"</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Version</span> <span class="nf">version</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">Version</span><span class="o">.</span><span class="na">unknownVersion</span><span class="o">();</span>
<span class="o">}</span>

}

3. 定义 META-INF.services 文件(可选)

通过 Java ServiceLoader 机制,Jackson 可以自动注册配置的 Module。在 com.fasterxml.jackson.databind.Module 配置文件里指定需要自动注册 Module 的全路径,多个 Module 可以写在多行。注意:services 配置文件必须为 com.fasterxml.jackson.databind.Module

src/main/resources/
├── META-INF
│ └── services
│ └── com.fasterxml.jackson.databind.Module

若没有配置 services 文件,则在调用 objectMapper.findAndRegisterModules() 时不能自动加载,需要通过 objectMapper.registerModule 方法手动注册,如:

objectMapper.registerModule(new ExampleModule());

Spring

Spring Boot 配置文件

spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
locale: zh_CN
serialization:
WRITE_DATES_WITH_ZONE_ID: false
WRITE_DURATIONS_AS_TIMESTAMPS: false
WRITE_DATES_AS_TIMESTAMPS: false
FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS: false
WRITE_ENUMS_USING_TO_STRING: true

WebFlux 加载 jackson-module-scala

添加依赖:

<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.12</artifactId>
<version>2.11.1</version>
</dependency>

configureHttpMessageCodecs 中配置 JsonDecoderJsonEncoder

@EnableWebFlux
@Configuration
public class CoreWebConfiguration implements WebFluxConfigurer {
@Autowired
private ObjectMapper objectMapper;
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">configureHttpMessageCodecs</span><span class="o">(</span><span class="n">ServerCodecConfigurer</span> <span class="n">configurer</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">ServerCodecConfigurer</span><span class="o">.</span><span class="na">ServerDefaultCodecs</span> <span class="n">defaultCodecs</span> <span class="o">=</span> <span class="n">configurer</span><span class="o">.</span><span class="na">defaultCodecs</span><span class="o">();</span>
    <span class="n">defaultCodecs</span><span class="o">.</span><span class="na">enableLoggingRequestDetails</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
    <span class="n">objectMapper</span><span class="o">.</span><span class="na">registerModule</span><span class="o">(</span><span class="k">new</span> <span class="n">DefaultScalaModule</span><span class="o">());</span>
    <span class="n">defaultCodecs</span><span class="o">.</span><span class="na">jackson2JsonDecoder</span><span class="o">(</span><span class="k">new</span> <span class="n">Jackson2JsonDecoder</span><span class="o">(</span><span class="n">objectMapper</span><span class="o">,</span> <span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">,</span> <span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_STREAM_JSON</span><span class="o">));</span>
    <span class="n">defaultCodecs</span><span class="o">.</span><span class="na">jackson2JsonEncoder</span><span class="o">(</span><span class="k">new</span> <span class="n">Jackson2JsonEncoder</span><span class="o">(</span><span class="n">objectMapper</span><span class="o">,</span> <span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">,</span> <span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_STREAM_JSON</span><span class="o">));</span>
<span class="o">}</span>

}

小结

为 Java 世界里众多 JSON 库选择烦恼?Jackson、Gson、Fastjson……不要犹豫,使用 Jackson!除了在 Spring 生态里开箱既用,在 Java 世界里也是最流行的。Jackson 除了支持 Scala 数据类型,还支持 Kotlin,对于 JVM 多语言开发,还有更好的选择吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值