参考截取翻译自以下链接
GitHub - FasterXML/jackson: Main Portal page for the Jackson project
Jackson被认为是”Java JSON库“或”Java最好的JSON解析器“。或简单地被当作”JSON for Java“
不仅如此,Jackson 还是一套用于 Java(和 JVM 平台)的数据处理工具,包括流式 JSON parser / generator库、匹配 data-binding 库(POJO和JSON相互转换),还有一个额外的 data format 模块来处理 Avro, BSON, CBOR, CSV, Smile, (Java) Properties, Protobuf, TOML, XML, YAML 这些数据编码,甚至还有大量的数据格式模块,来支持被广泛使用的数据类型如 Guava, Joda, PCollections 等等等
核心组件存在于他们自己的项目下 – 包括三个核心包(streaming, databind, annotations);数据格式库;数据类型库;JAX-RS provider;和一个复杂的扩展模块 – 这个project 连接各个模块的中心枢纽。
核心模块是扩展(模块)构建的基础。目前有3个模块 (Jackson 2.x为例) :
streaming
包; 它依赖于 streaming
和 annotations
包这个项目包含一般用途的 data-binding 功能和tree-model,它构建于 Streaming API (stream parser/generator) 包,并使用 Jackson Annotations配置。项目在 Apache License 2.0 下获得许可。
虽然Jackson的原始用途是 JSON data-binding,,但它现在也可以用于读取以其他数据格式编码的内容,只要parser 和 generator 存在即可。许多地方命名类都使用了“JSON”一词, 尽管对 JSON 格式没有实际硬性依赖。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!-- Use the latest version whenever possible. -->
<version>2.12.4</version>
</dependency>
这个包也同时依赖于jackson-core
和jackson-annotations
包,但是当使用构建工具像是Maven 或者Gradle时,会自动包含这两个依赖。你可以使用 jackson-bom 去保证依赖版本的兼容性。如果你不使用构建工具,你需要额外去下载这两个jar包。
更详细的文档可以去 Jackson-docs 仓库; 或这个项目的 Wiki
最常见的用法是获取一段 JSON,并从中构造一个普通的旧 Java 对象(“POJO”)。 所以让我们从简单两个参数的 POJO开始:
// Note: can use getters/setters as well; here we just use public fields directly:
public class MyValue {
public String name;
public int age;
// NOTE: if using getters/setters, can keep fields `protected` or `private`
}
我们需要一个 com.fasterxml.jackson.databind.ObjectMapper
实例,用于所有data-binding,所以让我们构建一个:
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
默认的实例适合我们使用了 – 我们将在之后学习如何配置 mapper 实例,用法很简单:
MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
// or:
value = mapper.readValue(new URL("http://some.com/api/entry.json"), MyValue.class);
// or:
value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);
相反,如果我们想从对象获取 JSON :
mapper.writeValue(new File("result.json"), myResultObject);
// or:
byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
// or:
String jsonString = mapper.writeValueAsString(myResultObject);
So far so good?
除了处理简单的 Bean 样式 POJO,您还可以处理 JDK Lists、Maps
Map<String, Integer> scoreByName = mapper.readValue(jsonSource, Map.class);
List<String> names = mapper.readValue(jsonSource, List.class);
// and can obviously write out as well
mapper.writeValue(new File("names.json"), names);
只要 JSON 结构匹配,并且类型简单。 如果有 POJO 值,则需要标明具体类型(注意:对于具有 List
等类型的 POJO 属性不需要这样做):
Map<String, ResultValue> results = mapper.readValue(jsonSource,
new TypeReference<Map<String, ResultValue>>() { } );
// why extra work? Java Type Erasure will prevent type detection otherwise
(note: 无论泛型类型如何,序列化都不需要额外的努力)
等一下! 还有一件事!
处理 Map
, List
和其他"简单" 对象类型(Strings, Numbers, Booleans) 会很简单, 对象遍历会很麻烦. 这个时候 Jackson的 Tree model 就派上用场啦:
// can be read as generic JsonNode, if it can be Object or Array; or,
// if known to be Object, as ObjectNode, if array, ArrayNode etc:
ObjectNode root = mapper.readTree("stuff.json");
String name = root.get("name").asText();
int age = root.get("age").asInt();
// can modify as well: this adds child Object as property 'other', set property 'type'
root.with("other").put("type", "student");
String json = mapper.writeValueAsString(root);
// with above, we end up with something like as 'json' String:
// {
// "name" : "Bob", "age" : 13,
// "other" : {
// "type" : "student"
// }
// }
Tree Model 可以比 data-binding 更便捷, 尤其是在结构高动态化的场景下, 或者是无法很好地映射 java类的时候.
还有一个规范处理模型,与 data-binding(序列化/反序列化POJO)一样方便,与Tree model一样灵活,就是:incremental (又称"streaming") model,即增量模型。它是底层处理模型,data-binding 和 Tree Model都建立在它的基础之上,但是它也对用户开放,使用它可以获得极致的性能,掌控解析/生成
的细节。
想更深入的理解,去看 Jackson Core component,但是现在先看个简单的例子来勾起你的求知欲
ObjectMapper mapper = ...;
// First: write simple JSON output
File jsonFile = new File("test.json");
// note: method added in Jackson 2.11 (earlier would need to use
// mapper.getFactory().createGenerator(...)
JsonGenerator g = f.createGenerator(jsonFile, JsonEncoding.UTF8);
// write JSON: { "message" : "Hello world!" }
g.writeStartObject();
g.writeStringField("message", "Hello world!");
g.writeEndObject();
g.close();
// Second: read file back
try (JsonParser p = mapper.createParser(jsonFile)) {
JsonToken t = p.nextToken(); // Should be JsonToken.START_OBJECT
t = p.nextToken(); // JsonToken.FIELD_NAME
if ((t != JsonToken.FIELD_NAME) || !"message".equals(p.getCurrentName())) {
// handle error
}
t = p.nextToken();
if (t != JsonToken.VALUE_STRING) {
// similarly
}
String msg = p.getText();
System.out.printf("My message to you is: %s!\n", msg);
}
您可能会使用两种入门级配置机制: Features 和 Annotations,即特性和注解
这里是一些你可能想要了解的feature配置
让我们开始进入高阶data-binding配置
// 序列化特性--改变 JSON 的编写方式
// 允许标准缩进 ("pretty-printing")
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// 允许“空POJO”(没有要序列化的属性)序列化
// (如果没有此设置,在这些情况下会引发异常)
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 以数字形式(timestamp时间戳)编写java.util.Date、Calendar
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 反序列化特性--更改 JSON 作为 POJO 的读取方式
// 防止遇到未知属性时出现异常
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 允许将 JSON 空字符串 ("") 强制转换为null对象值
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
此外,你可能需要改变一些低阶的解析、生成细节
// JsonParser.Feature,用于配置解析设置
// 允许 JSON 中的 C/C++ 样式注释(非标准,默认禁用)
// (note: with Jackson 2.5, there is also `mapper.enable(feature)` / `mapper.disable(feature)`)
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
// 允许在 JSON 中使用(非标准)不带引号的字段名称
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 允许使用单引号,非标准
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// JsonGenerator.Feature,配置低级 JSON 生成
// to force escaping of non-ASCII characters:
mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
Full set of features are explained on Jackson Features page.
最简单的基于注释的方法是使用 @JsonProperty
注解,像这样:
public class MyBean {
private String _name;
// without annotation, we'd get "theName", but we want "name":
@JsonProperty("name")
public String getTheName() { return _name; }
// note: it is enough to add annotation on just getter OR setter;
// so we can omit it here
public void setTheName(String n) { _name = n; }
}
There are other mechanisms to use for systematic naming changes: see Custom Naming Convention for details.
Note, too, that you can use Mix-in Annotations to associate all annotations.
There are two main annotations that can be used to to ignore properties: @JsonIgnore
for individual properties; and @JsonIgnoreProperties
for per-class definition
// means that if we see "foo" or "bar" in JSON, they will be quietly skipped
// regardless of whether POJO has such properties
@JsonIgnoreProperties({ "foo", "bar" })
public class MyBean
{
// will not be written as JSON; nor assigned from JSON:
@JsonIgnore
public String internal;
// no annotation, public field is read/written normally
public String external;
@JsonIgnore
public void setCode(int c) { _code = c; }
// note: will also be ignored because setter has annotation!
public int getCode() { return _code; }
}
As with renaming, note that annotations are “shared” between matching fields, getters and setters: if only one has @JsonIgnore
, it affects others. But it is also possible to use “split” annotations, to for example:
public class ReadButDontWriteProps {
private String _name;
@JsonProperty public void setName(String n) { _name = n; }
@JsonIgnore public String getName() { return _name; }
}
in this case, no “name” property would be written out (since ‘getter’ is ignored); but if “name” property was found from JSON, it would be assigned to POJO property!
For a more complete explanation of all possible ways of ignoring properties when writing out JSON, check “Filtering properties” article.
Unlike many other data-binding packages, Jackson does not require you to define “default constructor” (constructor that does not take arguments). While it will use one if nothing else is available, you can easily define that an argument-taking constructor is used:
public class CtorBean
{
public final String name;
public final int age;
@JsonCreator // constructor can be public, private, whatever
private CtorBean(@JsonProperty("name") String name,
@JsonProperty("age") int age)
{
this.name = name;
this.age = age;
}
}
Constructors are especially useful in supporting use of Immutable objects.
Alternatively, you can also define “factory methods”:
public class FactoryBean
{
// fields etc omitted for brevity
@JsonCreator
public static FactoryBean create(@JsonProperty("name") String name) {
// construct and return an instance
}
}
Note that use of a “creator method” (@JsonCreator
with @JsonProperty
annotated arguments) does not preclude use of setters: you can mix and match properties from constructor/factory method with ones that are set via setters or directly using fields.
Jackson一个很有用就是做任意POJO到POJO的转换。从概念上讲,你可以将转换视为 2 个步骤:第一步,把一个POJO写为JSON,第二步,把JSON绑定到另外一种类型的POJO。它的实现就是跳过实际存在的JSON生成,然后使用更高效的中间表示(intermediate representation)。
Conversions(转换)工作于在相互兼容的类型之间,且调用方式像这样简单:
ResultType result = mapper.convertValue(sourceObject, ResultType.class);
这里有几个可能有用的用例
// Convert from List<Integer> to int[]
List<Integer> sourceList = ...;
int[] ints = mapper.convertValue(sourceList, int[].class);
// Convert a POJO into Map!
Map<String,Object> propertyMap = mapper.convertValue(pojoValue, Map.class);
// ... and back
PojoType pojo = mapper.convertValue(propertyMap, PojoType.class);
// decode Base64! (default byte[] representation is base64-encoded String)
String base64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz";
byte[] binary = mapper.convertValue(base64, byte[].class);
基本上,Jackson可以代替很多Apache Commons 组件,像是base64 编/解码和处理to/from POJOs等任务
The Builder design pattern is a creational design pattern and can be used to create complex objects step by step. If we have an object that needs multiple checks on other dependencies, In such cases, it is preferred to use builder design pattern.
Let’s consider the person structure, which has some optional fields
public class Person {
private final String name;
private final Integer age;
// getters
}
Let’s see how we can employ its power in deserialization. First of all, let’s declare a private all-arguments constructor, and a Builder class.
private Person(String name, Integer age) {
this.name = name;
this.age = age;
}
static class Builder {
String name;
Integer age;
Builder withName(String name) {
this.name = name;
return this;
}
Builder withAge(Integer age) {
this.age = age;
return this;
}
public Person build() {
return new Person(name, age);
}
}
First of all, we need to mark our class with @JsonDeserialize
annotation, passing a builder parameter with a fully qualified domain name of a builder class. After that, we need to annotate the builder class itself as @JsonPOJOBuilder
.
@JsonDeserialize(builder = Person.Builder.class)
public class Person {
//...
@JsonPOJOBuilder
static class Builder {
//...
}
}
A simple unit test will be:
String json = "{\"name\":\"Hassan\",\"age\":23}";
Person person = new ObjectMapper().readValue(json, Person.class);
assertEquals("Hassan", person.getName());
assertEquals(23, person.getAge().intValue());
If your builder pattern implementation uses other prefixes for methods or uses other names than build() for the builder method Jackson also provide a handy way for you.
For example, if you have a builder class uses the “set” prefix for its methods and use the create() method instead of build() for building the whole class, you have to annotate your class like:
@JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
static class Builder {
String name;
Integer age;
Builder setName(String name) {
this.name = name;
return this;
}
Builder setAge(Integer age) {
this.age = age;
return this;
}
public Person create() {
return new Person(name, age);
}
}
Overall, Jackson library is very powerful in deserializing objects using builder pattern.