Jackson 是 Java 生态下的一款 JSON (返)序列化工具,具有高效、强大、安全(没有 Fastjson 那么多的安全漏洞)等特性。同时应用广泛,Spring Boot/Cloud、Akka、Spark 等众多框架都将其作为默认 JSON 处理工具。
依赖
要使用 Jackson,需要在项目中添加如下依赖(注:使用 Spring Boot 时不需要手动添加,Spring 框架已经默认包含):
Maven
1 2 3 4 5 6 7 8 9 10
| <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
1 2 3 4
| 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
1
| public static final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules()
|
Spring
1 2
| @Autowired private ObjectMapper objectMapper;
|
注:Spring 默认不会自动加载 classpath 路径的所有 Jackson Module。需要在 objectMapper
上调用 registerModule
方法手动注册。
Scala
1
| val objectMapper = new ObjectMapper().findAndRegisterModules()
|
建议的 Jackson 配置
编码配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ObjectMapper objectMapper = new ObjectMapper() .findAndRegisterModules() .configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, false) .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) .configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false) .configure(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, false) .configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true) .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")) .setTimeZone(SimpleTimeZone.getTimeZone("GMT+8"));
|
创建 JSON 对像
Jackson 的 ArrayNode
和 ObjectNode
对象均不能直接创建,需要通过 objectMapper
来创建。同时,两个 Node 对像都 JsonNode
的子类。
1 2 3 4 5
| ArrayNode jsonArray = objectMapper.createArrayNode(); jsonArray.add("Jackson").add("JSON"); ObjectNode jsonObject = objectMapper.createObjectNode() .put("title", "Json 之 Jackson") .put("readCount", 1024);
|
反序列化
1 2 3 4 5 6 7 8 9 10
| String jsonText = ....;
JsonNode javaTimeNode = objectMapper.readTree(jsonText);
JavaTime javaTime1 = objectMapper.readValue(jsonText, JavaTime.class);
JavaTime javaTime2 = objectMapper.treeToValue(javaTimeNode, JavaTime.class);
|
序列化
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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));
|
输出:
1 2 3
| {"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
1
| JsonNode jsonNode = objectMapper.valueToTree(javaTime);
|
美化输出
1
| objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(javaTime)
|
1 2 3 4 5 6 7 8 9 10
| { "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
1 2
| @JsonIgnore private Long _version;
|
@JsonIgnore
注解也可以添加在 getter 函数上。
@JsonIgnoreProperties
1 2 3
| @JsonIgnoreProperties({"_version", "timestamp"}) public class JavaTime { }
|
自定义序列化、反序列化器
对于某些自定义类型或 Jackson 不支持的类型,可以实现自己的序列化、反序列化器。
POJO
在类型上使用 @JsonSerialize
和 @JsonDeserialize
注解来分别指定序列化和反序列化器。
1 2 3 4 5 6 7 8
| @Data public class User { private String id;
@JsonSerialize(using = PgJsonSerializer.class) @JsonDeserialize(using = PgJsonDeserializer.class) private io.r2dbc.postgresql.codec.Json metadata; }
|
先将 R2DBC PostgreSQL 的 JSON 转化为 JsonNode 对象,再调用 gen.writeTree
对其进行序列化。这样才能保证序列化出来的字段值是一个 JSON 对象或 JSON 数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import io.r2dbc.postgresql.codec.Json;
public class PgJsonSerializer extends StdSerializer<Json> { public PgJsonSerializer() { super(Json.class); }
@Override public void serialize(Json value, JsonGenerator gen, SerializerProvider provider) throws IOException { JsonParser parser = gen.getCodec().getFactory().createParser(value.asArray()); JsonNode node = gen.getCodec().readTree(parser); gen.writeTree(node); } }
|
通过 ObjectCodec#readTree
将 JsonParse
读取为一个 TreeNode
对像,再将其序列化为 JSON 格式(JSON 字符串的字符数组形式)后传给 Json.of
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import io.r2dbc.postgresql.codec.Json;
public class PgJsonDeserializer extends StdDeserializer<Json> { public PgJsonDeserializer() { super(Json.class); }
@Override public Json deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { TreeNode node = p.getCodec().readTree(p); ObjectMapper objectMapper = (ObjectMapper) p.getCodec(); byte[] value = objectMapper.writeValueAsBytes(node); return Json.of(value); } }
|
Jackson Module
当自定义(反)序列化变多时,在每个类上通过注解手工指定(反)序列化器就变得很繁琐。我们可以通过定义一个 Jackson Module 并使用 .findAndRegisterModules()
通过 Java 的 Service 机制自动注册到 ObjectMapper
。通过 Module 机制注册了类型的序列化、反序列化器后,不在需要在属性或方法上定义 @JsonSerialize
和 @JsonDeserialize
注解。
1. 定义 Serializers 和 Deserializers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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(); }
return super.findBeanDeserializer(type, config, beanDesc); } }
|
2. 实现 Module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class ExampleModule extends com.fasterxml.jackson.databind.Module { @Override public void setupModule(SetupContext context) { context.addSerializers(new ExampleSerializers()); context.addDeserializers(new ExampleDeserializers()); }
@Override public String getModuleName() { return "ExampleModule"; }
@Override public Version version() { return Version.unknownVersion(); } }
|
3. 定义 META-INF.services
文件(可选)
通过 Java ServiceLoader 机制,Jackson 可以自动注册配置的 Module。在 com.fasterxml.jackson.databind.Module
配置文件里指定需要自动注册 Module 的全路径,多个 Module 可以写在多行。注意:services 配置文件必须为 com.fasterxml.jackson.databind.Module
。
1 2 3 4
| src/main/resources/ ├── META-INF │ └── services │ └── com.fasterxml.jackson.databind.Module
|
若没有配置 services 文件,则在调用 objectMapper.findAndRegisterModules()
时不能自动加载,需要通过 objectMapper.registerModule
方法手动注册,如:
1
| objectMapper.registerModule(new ExampleModule());
|
Spring
Spring Boot 配置文件
1 2 3 4 5 6 7 8 9 10 11
| 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
添加依赖:
1 2 3 4 5
| <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-scala_2.12</artifactId> <version>2.11.1</version> </dependency>
|
在 configureHttpMessageCodecs
中配置 JsonDecoder
和 JsonEncoder
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @EnableWebFlux @Configuration public class CoreWebConfiguration implements WebFluxConfigurer { @Autowired private ObjectMapper objectMapper;
@Override public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) { ServerCodecConfigurer.ServerDefaultCodecs defaultCodecs = configurer.defaultCodecs(); defaultCodecs.enableLoggingRequestDetails(true); objectMapper.registerModule(new DefaultScalaModule()); defaultCodecs.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON, MediaType.APPLICATION_STREAM_JSON)); defaultCodecs.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON, MediaType.APPLICATION_STREAM_JSON)); } }
|
小结
为 Java 世界里众多 JSON 库选择烦恼?Jackson、Gson、Fastjson……不要犹豫,使用 Jackson!除了在 Spring 生态里开箱既用,在 Java 世界里也是最流行的。Jackson 除了支持 Scala 数据类型,还支持 Kotlin,对于 JVM 多语言开发,还有更好的选择吗?