jackson是java中最常用的json处理工具包之一,其他类似功能的包括gson和fastjson等。本文将简单介绍jackson的基础知识,以作为各位读者未来使用的参考。
三种使用方式
jackson有三种处理json的方式,分别为
- data binding
- tree model
- streaming api
data binding
这种方式可以在json字符串和pojo对象之间直接进行转换。比如说我们有一个json字符串
{"firstName":"dizzy","lastName":"dwarf"}
通过这种方式我们可以直接将其转换成一个Person类的实例,其中firstName和lastName是Person类定义的两个成员变量。
ObjectMapper objectMapper = new ObjectMapper();
Person person = objectMapper.readValue(jsonStr, Person.class);
tree model
这种方式类似于xml的DOM解析,在json字符串和DOM树之间进行转换,DOM树的节点是JsonNode
类型。其优点在于以统一的方式看待json字符串中的各个部分,使用起来更灵活。
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(jsonStr);
JsonNode firstName = root.path("firstName");
构建DOM树
如何构建JsonNode
的DOM树呢?JsonNode
是抽象类,需要使用ObjectNode
和ArrayNode
等子类。
ObjectNode objectNode = objectMapper.createObjectNode();
ArrayNode arrayNode = objectMapper.createArrayNode();
streaming api
这种方式类似于xml的SAX解析,每次处理一个事件,或者这里叫token更合适。
streaming api反序列化
反序列化用的是JsonParser
,它的使用方式和迭代器非常相似。你每次处理的都是一个token,比如说在上面这个json字符串中,包括{、firstName、dizzy、lastName、dwarf、}等6个token,通过调用nextToken
方法可以获得下一个token。
streaming api序列化
序列化用的JsonGenerator
,它的方法都非常直观,比如说writeStartObject
、writeStringField
、writeEndObject
等,这里就不具体介绍了。
注解
通过注解可以定制jackson的各种特性,这里只介绍最常用的几个。
序列化注解
-
@JsonGetter
注解在方法上,将方法返回的值作为字段序列化的值
public class Person {
private String firstName;
@JsonGetter("firstName")
public String getFirstName() {
return firstName + "_modified";
}
// 省略了setter方法
}
这样一个firstName为"dizzy"的Person序列化后就变成了
{"firstName":"dizzy_modified"}
-
@JsonSerialize
注解在成员变量上,使用指定的JsonSerializer
实现类来序列化这个字段,这个实现类最关键的是serialize
方法,这个方法会为你提供JsonGenerator
对象作为参数,让你可以通过它来构建序列化后的值。 -
@JsonValue
注解在方法上,将方法返回的值作为整个对象序列化的结果。
反序列化注解
-
@JsonSetter
@JsonGetter
的逆过程 -
@JsonDeserialize
@JsonSerialize
的逆过程 -
@JsonAlias
默认情况下java对象中的成员变量名和json字符串的字段名是一对一关系的,但是可能存在这样一种情况。比如firstName这个成员变量,可能json字符串有不同的来源,有的地方这个字段叫firstName,另外一些地方传的字段名称是fName。这个时候就可以用@JsonAlias
使这个成员变量接受更多的名称。
通用注解
-
@JsonProperty
指定该成员变量对应的json字符串的字段名,默认情况下如果两者相同的话不需要使用该注解。 -
@JsonIgnore
指定该成员变量不参与序列化和反序列化
具体问题解决
下划线和驼峰转换
一般情况下java变量命名采用驼峰方式,而json字符串可能采用下划线方式。解决方式为在类或者成员变量上增加以下注解
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
需要注意的是虽然反序列化的时候first_name会对应到firstName变量,但是序列化的时候也会输出为first_name
多态处理
有时候我们希望根据json字符串中某个字段的取值反序列化成不同的子类,比如说type为1是Student,type为2是Teacher,注意Student和Teacher必须继承同一个父类Person。
@JsonTypeInfo(use = JsonTypeInfo.ID.NAME, property = "type", defaultImpl = Person.class)
@JsonSubTypes({
@JsonSubTypes.Type(value = Student.class, name = "1"),
@JsonSubTypes.Type(value = Teacher.class, name = "2")
})
public class Person {}
- property是决定子类型的字段名称
- defaultImpl是默认情况下反序列化的类型,这里指的是当type不为1和2时
- name是子类型对应的字段取值
带泛型参数的List和Map的反序列化
如果我们希望ObjectMapper.readValue返回List<Person>或者Map<String, Person>,由于List<Person>.class和Map<String, Person>.class在java中是不合法的,需要借助于TypeReference
TypeReference<List<Person>> typeReference = new TypeReference<List<Person>>(){};
List<Person> list = objectMapper.readValue(jsonStr, typeReference);
jackson如何集成Spring
Spring提供的MappingJackson2MessageConverter
类封装了ObjectMapper
,如果希望对ObjectMapper
进行定制,可以自己生成一个MappingJackson2MessageConverter
对象并注册为bean
只对部分成员变量序列化同时不影响反序列化
有时候我们希望只对部分成员变量进行序列化,如果用@JsonIgnore
,会同时影响反序列化。这个时候我们可以用@JsonView
注解指定某个视图类的序列化结果包含该成员变量。