Java 第三方包-Jackson

1. wiki

用于 Json 和 XML(基于jackson-dataformat-xml) 与 JavaBean 之间的序列化和反序列化

2. 模块

  • jackson-core:核心包,定义了低级流(Streaming) API,提供基于“流模式”解析。Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json;
  • jackson-annotations:注解包,提供标准的 Jackson 注解功能;
  • jackson-databind:数据绑定包,实现了数据绑定(和对象序列化)支持,它依赖于 Streaming 和 Annotations 包。提供基于“对象绑定”解析的 API(ObjectMapper) 和“树模型”解析的 API(JsonNode),两者则依赖基于“流模式”解析的API。

一般必须引入以下3个依赖

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
29
30
31
32
33
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!-- JavaBean 中的时间字段使用的是JDK8新增的时间日期需要引入 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.8</version>
</dependency>
<!-- 解析 XML 需要引入 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.8</version>
</dependency>
<!-- 解析 yaml 需要引入 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.9.8</version>
</dependency>

3. 核心类

3.1. ObjectMapper

常用功能:

  • 从字符串、流或文件中解析 JSON,并创建表示已解析的 JSON 的 Java 对象(反序列化)。
  • 将 Java 对象构建成 JSON 字符串(序列化)。
  • 将 JSON 解析为自定义类的对象,也可以解析 JSON 树模型的对象。

一般遵循已下规则的属性都可以被序列化和反序列化:

  • public 修饰的属性可序列化和反序列化。
  • 属性提供 public 的 getter/setter 方法,该属性可序列化和反序列化。
  • 属性只有 public 的 setter 方法,而无 public 的 getter 方法,该属性只能用于反序列化。

3.1.1. JavaBean 与 json

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
29
30
31
private static ObjectMapper mapper = new ObjectMapper();

// JavaBean转JSON字符串
WeChat weChat = new WeChat();
weChat.setId("hello");
weChat.setName("world");
weChat.setInterest(new String[]{"Java", "Python", "C"});
String result = mapper.writeValueAsString(weChat);
System.out.println(result);

// JSON字符串转JavaBean
String json = "{\"id\":\"hello\",\"name\":\"world\",\"interest\":[\"Java\",\"Python\",\"C\"]}";
WeChat weChat = mapper.readValue(json, WeChat.class);
System.out.println(weChat);

// JSON字符串转Map、集合,对泛型的反序列化,可以使用 TypeReference 可以明确的指定反序列化的类型。
String json = ...;
Map<String, Object> map1 = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});
List<WeChat> list1 = mapper.readValue(jsonStr, new TypeReference<List<WeChat>>() {});
Map<String, Object> map2 = mapper.readValue(json, Map.class);
List<WeChat> list2 = mapper.readValue(jsonStr, List.class);

// JavaBean转文件
WeChat weChat = ...;
mapper.writeValue(new File("/json.txt"), weChat);//写到文件
WeChat weChat1 = mapper.readValue(new File("/json.txt"), WeChat.class);//从文件中读取

// JavaBean转字节流
WeChat weChat = ...;
byte[] bytes = mapper.writeValueAsBytes(weChat);// 写为字节流
WeChat weChat1 = mapper.readValue(bytes, WeChat.class);// 从字节流读取

3.1.2. JSON 树模型

JSON树模型,用于字符串比较大但不是所有字段都需要时,灵活的获取所需的字段内容。其中 get 方法和 path 功能相似,区别在于:

  • 如果要读取的 key 在Json串中不存在时,get 方法会 null,
  • 而 path 会返回 MissingNode 实例对象,在链路方法情况下保证不会抛出异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// JavaBean转JSON字符串
// 1.构建JSON树
ObjectNode root = mapper.createObjectNode();
root.put("id", "hello");
root.put("name", "world");
ArrayNode interest = root.putArray("interest");
interest.add("Java");
interest.add("Python");
interest.add("C");
// 2.JSON树转JSON字符串
String json = mapper.writeValueAsString(root);

// 解析JSON字符串为JSON树模型
String json = ...;
JsonNode jsonNode = mapper.readTree(json);
String name = jsonNode.path("name").asText();
JsonNode interestNode = jsonNode.get("interest");
if (interestNode.isArray()){
for (JsonNode node : interestNode){
System.out.println(node.asText());
}
}

3.1.3. 格式化统一配置

可以统一配置一些需要的参数。在2.2版本中新增了一个 ObjectMapper 的实现类 JsonMapper,功能与 ObjectMapper 一致,但是可以使用链式配置

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
static{
// 转换为格式化的 json 显示出来的格式美化
mapper.enable(SerializationFeature.INDENT_OUTPUT);

//序列化的时候序列对象的那些属性
//JsonInclude.Include.ALWAYS 所有属性
//JsonInclude.Include.NON_DEFAULT 属性为默认值不序列化
//JsonInclude.Include.NON_EMPTY 属性为 空(“”) 或者为 NULL 都不序列化
//JsonInclude.Include.NON_NULL 属性为NULL 不序列化
mapper.setSerializationInclusion(Include.ALWAYS);

//如果是空对象的时候,不抛异常
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

// 忽略 transient 修饰的属性
mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);

//修改序列化后日期格式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

//处理不同的时区偏移格式
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.registerModule(new JavaTimeModule());

//反序列化时,遇到没有的属性就报错=true,否则 false
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

3.2. 常用注解

  • @JsonProperty:作用JavaBean字段上,指定一个字段用于JSON映射,默认情况下映射的JSON字段与注解的字段名称相同。可通过value属性指定映射的JSON的字段名称
  • @JsonIgnore:可用于字段、getter/setter、构造函数参数上,指定字段不参与序列化和反序列化
  • @JsonValue:反序列化时,指定将枚举字段反序列化为什么值
  • @JsonIgnoreProperties:作用于类上,序列化时@JsonIgnoreProperties({"prop1", "prop2"})会忽略 pro1 和 pro2 两个属性。反序列化时@JsonIgnoreProperties(ignoreUnknown=true)会忽略类中不存在的字段。
  • @JsonFormat:作用于字段上,通常用来进行格式化操作。@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

3.3. 自定义解析器

可自定义解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyFastjsonDeserialize extends JsonDeserializer<Point> {

@Override
public Point deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
Iterator<JsonNode> iterator = node.get("coordinates").elements();
List<Double> list = new ArrayList<>();
while (iterator.hasNext()) {
list.add(iterator.next().asDouble());
}
return new Point(list.get(0), list.get(1));
}
}

定义完成之后,注册到Mapper中:

1
2
3
4
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Point.class, new MyFastjsonDeserialize());
objectMapper.registerModule(module);

3.4. 解析 XML

1
2
3
4
5
// JavaBean转XML
WeChat weChat = ...;
XmlMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(weChat);
System.out.println(xml);

结果

1
2
3
4
5
6
7
8
9
<WeChat>
<id>hello</id>
<name>world</name>
<interest>
<interest>Java</interest>
<interest>Python</interest>
<interest>C</interest>
</interest>
</WeChat>

3.5 @JsonTypeInfo 及相关注解

1. @JsonTypeInfo

作用在接口或类上,被用来开启多态类型的处理,对基类、接口、子类、实现类都有效,语法为 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = “name”)

  • use:定义使用哪一种类型识别码,可选值:
    • JsonTypeInfo.Id.CLASS:使用完全限定类名做识别
    • JsonTypeInfo.Id.MINIMAL_CLASS:若基类和子类在同一包类,使用忽略包名的类名作为识别码
    • JsonTypeInfo.Id.NAME:一个合乎逻辑的指定名称
    • JsonTypeInfo.Id.CUSTOM:自定义识别码,由 @JsonTypeIdResolver 对应,稍后解释
    • JsonTypeInfo.Id.NONE:不使用识别码
  • include:可选,指定识别码是如何被包含进去的,可选值:
    • JsonTypeInfo.As.PROPERTY:作为数据的兄弟属性
    • JsonTypeInfo.As.EXISTING_PROPERTY:作为 POJO 中已经存在的属性,需要手动 set
    • JsonTypeInfo.As.EXTERNAL_PROPERTY:作为扩展属性
    • JsonTypeInfo.As.WRAPPER_OBJECT:作为一个包装的对象
    • JsonTypeInfo.As.WRAPPER_ARRAY:作为一个包装的数组
  • property:可选,制定识别码的属性名称,此属性只有如下情况时才有效:
    • useJsonTypeInfo.Id.CLASS(默认为 @class)、JsonTypeInfo.Id.MINIMAL_CLASS(默认为@c)、JsonTypeInfo.Id.NAME(默认为 @type)
    • includeJsonTypeInfo.As.PROPERTY、JsonTypeInfo.As.EXISTING_PROPERTY、JsonTypeInfo.As.EXTERNAL_PROPERTY
  • defaultImpl:可选,如果类型识别码不存在或者无效,可以使用该属性来制定反序列化时使用的默认类型
  • visible:可选,定义了类型标识符的值是否会通过 JSON 流成为反序列化器的一部分,默认为 fale,也就是说,jackson 会从 JSON 内容中处理和删除类型标识符再传递给 JsonDeserializer

2. @JsonSubTypes

作用于类或接口,用来列出给定类的子类,只有当子类类型无法被检测到时才会使用它,一般是配合 @JsonTypeInfo 在基类上使用。

1
2
3
4
@JsonSubTypes({
@JsonSubTypes.Type(name="Dog", value=Dog.class)
@JsonSubTypes.Type(name="Cat", value=Cat.class)
})
  • 它值是一个 @JsonSubTypes.Type[] 数组,里面枚举了多态类型
  • value 为相应子类
  • name 为类型的标识符,对应 @JsonTypeInfo 中的 property 标识名称的值,此为可选值,若不指定需由 @JsonTypeName 在子类上指定)

3. @JsonTypeName

作用于子类,用来为多态子类指定类型标识符的值。

1
2
3
4
5
6
7
8
9
10
11
12
@JsonTypeInfo(
use = Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "animalType",
visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(name="Dog", value=Dog.class),
@JsonSubTypes.Type(name="Cat", value=Cat.class),
})
public class Animal{
private String animalType;
}

等效于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 基类
@JsonTypeInfo(
use = Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "animalType",
visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value=Dog.class),
@JsonSubTypes.Type(value=Cat.class),
})
public class Animal{
private String animalType;
}

// 子类
@JsonTypeName("Dog")
public class Dog extends Animal {}

4. @JsonTypeIdResolver

通过 @JsonSubtypes 来实现多态序列化,在有些情况下会比较麻烦,如新增子类的时候,需要添加一下 @JsonSubTypes。此时,可以使用 JsonTypeInfo.Id.CUSTOM 策略,然后实现自定义实现 TypeIdResolve

1
2
3
4
5
6
// 基类
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type")
@JsonTypeIdResolver(AnimalTypeIdResolver.class)

// 自定义实现类
public class AnimalTypeIdResolver extends TypeIdResolverBase {}

其中有主要的 2 个重要方法:

  • idFromValueAndType:序列化的时,如何生成标识符
  • typeFromId:是反序列化的时,如何根据标识符来识别到具体类型
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
29
30
31
32
33
public class AnimalTypeIdResolver extends TypeIdResolverBase {
@Override
public String idFromValue(Object value) {
return idFromValueAndType(value, value.getClass());
}

@Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
if (suggestedType.isAssignableFrom(Cat.class)) {
return "cat";
} else if (suggestedType.isAssignableFrom(Dog.class)) {
return "dog";
} else if (suggestedType.isAssignableFrom(Wolf.class)) {
return "wolf";
}
return null;
}

@Override
public JavaType typeFromId(DatabindContext context, String id) {
if ("cat".equals(id)) {
return context.constructType(Cat.class);
} else if ("dog".equals(id)) {
return context.constructType(Dog.class);
}
return null;
}

@Override
public JsonTypeInfo.Id getMechanism() {
return JsonTypeInfo.Id.NAME;
}
}