共计 5042 个字符,预计需要花费 13 分钟才能阅读完成。
本文是对 Jackson 的疾速入门介绍,次要分为四局部:
- 根本应用
- 根底配置
- 自定义序列化 / 反序列化
- 对泛型的解决
下面这几个话题足以笼罩日常开发的场景了。限于篇幅所限,本文力求读者读完后能把握 Jackson 在日常应用中的绝大部分场景,以及理解如何着手摸索 Jackson 的深层定制。
根本应用
引入 Jackson
本文假如读者相熟 Maven 的应用,那么只须要在我的项目中增加上面的依赖关系就能够了:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>[VERSION]</version>
</dependency>
最新的版本号请在这里查看。
ObjectMapper
Jackson 提供了 com.fasterxml.jackson.databind.ObjectMapper
类作为 JSON 操作的对立接口。这个类有两个特点:
- 实例化代价很高;
- 线程平安。
因而咱们应该防止频繁大量的创立 ObjectMapper
实例,而应该利用单例模式,比方:
- 将
ObjectMapper
对象放在动态成员中; - 将
ObjectMapper
对象放在 IoC 容器中。这样做的益处是能够参加主动组装,害处是容器外拜访略微有点麻烦。
咱们晓得 JSON 文档是一个树形数据结构。对于任何一个 JSON 框架,必然会在外部实现一套树节点,以不便 JSON 文档的解析和生成。因而一个 JSON 框架的基本功能就是实现上面三点:
- JSON 字符串和 POJO 对象之间的转换;
- JSON 字符串和树节点之间的转换;
- 树节点和 POJO 对象之间的转换。
Jackson 实现的树节点类叫做 com.fasterxml.jackson.databind.JsonNode
。尽管树结构是框架外部的货色,但如果咱们要对序列化 / 反序列化进行自定义,还是要理解树节点的操作。
基本功能
ObjectMapper
提供了简略好用的接口来实现下面三个性能。上面是应用示例:
// 假如咱们有一个叫做 Book 的类,外面只有一个 name 属性
String json = "{\"name\":\"<<Design Patterns>>\"}";
ObjectMapper objectMapper = new ObjectMapper();
Book book = objectMapper.readValue(json, Book.class); // JSON -> OBJECT
String json2 = objectMapper.writeValueAsString(book); // OBJECT -> JSON
JsonNode node = objectMapper.readTree(json); // JSON -> NODE
String json3 = objectMapper.writeValueAsString(node); // NODE -> JSON
Book book2 = objectMapper.treeToValue(node, Book.class); // NODE -> OBJECT
JsonNode node2 = objectMapper.valueToTree(book); // OBJECT -> NODE
以上就是 Jackson 的根本应用办法。
根底配置
ObjectMapper 提供十分多的配置,本文篇幅无限无奈一一介绍,只能挑几个重点。并且在具体介绍之前,最好先带读者理解 ObjectMapper 的整体配置是如何布局和拜访的,以便读者进一步摸索 ObjectMapper。
ObjectMapper 的配置大抵分为个性开关、配置属性、性能替换和扩大模块四种类别。
个性开关
个性开关通过 ObjectMapper 的 configure()
办法来调用,上面是一个例子:
// 以多行缩进格式化的格局输入 JSON
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// 反序列化若遇到无奈辨认的属性,不抛出异样
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
configure()
办法的第一个参数就是个性枚举值。所有的个性都在上面五个枚举类中定义:
- com.fasterxml.jackson.core.JsonGenerator.Feature
- com.fasterxml.jackson.core.JsonParser.Feature
- com.fasterxml.jackson.databind.MapperFeature
- com.fasterxml.jackson.databind.SerializationFeature
- com.fasterxml.jackson.databind.DeserializationFeature
倡议读者有工夫的话,浏览一遍这些枚举值(源码中有详尽的正文),理解 ObjectMapper 提供哪些个性开关。
配置属性
当然有些配置是不能简略地开关的,所以 ObjectMapper 提供 setter 办法来设置这些配置属性。上面是一个例子:
// 当输入 JSON 时不蕴含 null 属性
objectMapper.setDefaultPropertyInclusion(Include.NON_NULL);
// 自定义序列化和反序列化的日期格局
objectMapper.setDateFormat(new SimpleDateFormat("yyyyMMddHHmmssSSS"));
// 设置属性命名格调
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
性能替换
ObjectMapper 容许你用本人的实现来替换它的某些性能,比方 setNodeFactory(JsonNodeFactory)
、setSerializerFactory(SerializerFactory)
、setTypeFactory(TypeFactory)
等等,这些办法看名字就晓得是用于深度定制的,平时咱们用不到。
扩大模块
ObjectMapper 提供一个叫 registerModule(Module)
的办法。Module 是一个抽象类,目标是帮忙用户将特定场景下对 ObjectMapper 的一整套定制过程集中在一个类外面。所以除非是大规模的定制,否则是没必要用它的。
自定义序列化 / 反序列化
然而在某些状况下,下面这些配置仍旧不能满足咱们的须要。上面是一个例子,假如咱们有一套这样的 POJO 类:
// 假如读者晓得如何应用 lombok,这里用 @Data 来代替 getter/setter 办法
// Person.java
@Data
public class Person {private Pet pet;}
// Pet.java
@Data
public abstract class Pet {private String name;}
// Dog.java
public class Dog extends Pet {
}
// Cat.java
public class Cat extends Pet {}
以及这样一个 JSON 字符串 {"pet":{"name":"Matt"}}
,如果我想把它反序列化为 Person 对象,就会失败,因为 ObjectMapper 无奈通过 JSON 内容来判断 pet 属性值应该是一个 Dog 对象还是 Cat 对象。这时候咱们就要用到自定义反序列化了。上面是解决这个问题的过程:
1. 补完类型信息
反序列化失败归根结底是因为 JSON 中短少类型信息,所以要有一种形式来将它补全。咱们增加一个新的属性 type
,如果属性值存在,则依据它来确定类型;如果不存在,则应用一个默认的类型(比方 Cat)。当属性存在时,JSON 字符串是这样的:
{"pet":{"name":"Matt","type":"Dog"}}
同时 Pet 对象生成的 JSON 也要蕴含 type 属性。Pet 类代码改变如下:
// Pet.java
@Data
public abstract class Pet {
private String name;
public String getType() {return this.getClass().getSimpleName();}
}
2. 自定义反序列化
首先咱们通过注解通知 ObjectMapper,pet 属性要用自定义的反序列化类。对 Person 类的代码改变如下:
// Person.java
@Data
public class Person {@JsonDeserialize(using = PetDeserializer.class)
private Pet pet;
}
PetDeserializer 这个类尚不存在,咱们须要创立它:
// PetDeserializer.java
public class PetDeserializer extends JsonDeserializer<Pet> {
@Override
public Pet deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// 1. 取 JSON 中的 type 属性,留神这里是通过树节点属性来取值
JsonNode node = p.readValueAsTree();
String petType = node.get("type").asText();
// 2. 依据 type 的值来决定返回 Dog 对象还是 Cat 对象。// 留神这里是如何间接反序列化整个树节点的
Pet pet;
if (Cat.class.getSimpleName().equals(petType)) {pet = p.getCodec().treeToValue(node, Cat.class);
} else if (Dog.class.getSimpleName().equals(petType)) {pet = p.getCodec().treeToValue(node, Dog.class);
} else {pet = p.getCodec().treeToValue(node, Cat.class);
}
return pet;
}
}
不过当 JSON 字符串蕴含 type 属性时,运行这段代码依旧会反序列化失败,因为 Pet 类并没有真正的 type 属性。没关系咱们再给 getType()
办法加上一个 @JsonIgnore
注解。对 Pet 类的改变如下:
// Pet.java
@Data
public abstract class Pet {
private String name;
@JsonIgnore
public String getType() {return this.getClass().getSimpleName();}
}
这样 Person 类就能失常序列化和反序列化了。
自定义反序列化须要继承 JsonDeserializer
类,而自定义序列化则须要继承 JsonSerializer
类,这里不再举例了。
对泛型的解决
这里针对的是无奈进行类型推断的泛型,介绍如何将 JSON 以指定的元素类型反序列化到一个汇合。假如咱们有一个这样的类:
@Data
public class Book {private String name;}
以及这样一个 JSON 字符串:
[{"name":"book1"}]
想要将其序列化为一个 List<Book>
对象,须要采取上面的形式,不同的 jackson 版本有区别:
// jackson 2.11 以下版本
CollectionType typeRef =
com.fasterxml.jackson.databind.type.TypeFactory
.defaultInstance()
.constructCollectionType(List.class, Book.class);
List<Book> bookList =
objectMapper.readValue(json, typeRef);
// jackson 2.11 及以上版本
List<Book> bookList = objectMapper
.readerForListOf(Book.class).readValue(json);
以上就是对 Jackson 的疾速入门介绍,如有不正确之处,感激斧正!