介绍
Jackson-jr 是一个轻量级的Java JSON 处理库。这个库被设计用来替代 Jackson 的复杂性。对比 Jackson 的复杂 API,Jackson-jr 的启动速度更快,包大小更小。
虽然Jackson databind(如ObjectMapper)是通用数据绑定的良好选择,但它的占用空间(Jar包大小)和启动开销在某些领域可能存在问题:比如移动端,特别是对于轻量使用(读或写)。这种情况下,完整的Jackson API是让人接受不了的。
由于所有这些原因,Jackson 官方决定创建一个更简单、更小的库:Jackson-jr。它仍旧构建在 Streaming API 之上,但不依赖于 databind 和annotation。因此,它的大小(jar和运行时内存使用)要小得多,它的API非常紧凑,所以适合APP等移动端。
它仅仅只依赖了 jackson-core 模块,所以体积上控制得非常的好。Jackson 单单三大核心模块大小合计1700KB左右(320 + 70 + 1370)。而Jackson-jr的体积控制在了95KB(就算加上core模块的320也不到500KB)。
针对实际开发的情况,很多时候我们只需要对 JSON 数据进行读取和写入,至于一些过于复杂和强大的属性,我们可能也用不上,因此针对一些 JSON 数据使用场景比较单一的情况,Jackson-jr 就显得更有优势了。
所以,Jackson-jr 的主要情况就是在针对 JSON 格式的读写上面,如果你只需要简单的读和些,Jackson-jr 通常是你的一个选择。
在本文中,我们对 Jackson-jr 的简单使用进行一些说明和示例。
开始使用 Jackson-jr
Jackson-jr 提供的是轻量和高效的 JSON 处理模块。所以 Jackson-jr 针对 JSON 数据结构只提供了简单的数据读取和写入功能。
当然 Jackson-jr 是可以处理对象和数组的。
在开始使用 Jackson-jr 之前,我们需要把需要的包添加到 Maven 项目的 pom 文件中。
当前的可以使用的最新版本为:2.17.0
<dependency>
<groupId>com.fasterxml.jackson.jr</groupId>
<artifactId>jackson-jr-all</artifactId>
<version>2.17.0</version>
</dependency>
XML
针对 Gradle 可以使用不同的表达方式,唯一需要注意一点的就是版本号。
处理 JSON 对象
尽管我们是使用 Jackson-jr 来处理 JSON 字符串,和 Jackson 一样,字符串也会被处理成为 JSON 对象:每一个 JSON 实例是不可变(immutable )的,同时也是线程安全的。
正是因为有上面的特性,我们可以在多线程程序,单例模式,Spring Bean 中安全的使用创建的 JSON 对象。
创建 JSON 对象和数组
针对创建的 JSON 对象,我们可以使用类似 LinkedHashMap 来创建对象,对于数组,我们就可以用我们常用的 ArrayList。
因为 JSON 对象使用的是 LinkedHashMap,所以我们可以利用 Map 的特性,使用 LinkedHashMap.put()
方法来 Push 数据到 Map 中。
@Test
public void createJsonStringTest() throws IOException {
System.out.println(JSON.std.with(JSON.Feature.PRETTY_PRINT_OUTPUT)
.asString(new LinkedHashMap<String, Object>() {{
put("name", "John Doe");
put("age", 30);
}}));
}
Java
Jackson-jr Composer
Jackson-jr 同时还提供了 Composer 的这个创建方法,这个创建方法更加直接。
你可以按照你的想法直接进行定义和输出,在这之前,我们通常需要定义一个对象,然后对对象进行完成数据后再输出,对于一些简单的 JSON 格式。我们可以不用定义对象了,而是直接使用。
public static String jsonComposer() throws IOException {
return JSON.std.with(JSON.Feature.PRETTY_PRINT_OUTPUT)
.composeString()
.startObject()
.startArrayField("objectArray")
.startObject()
.put("name", "name1")
.put("age", 11)
.end()
.startObject()
.put("name", "name2")
.put("age", 12)
.end()
.end()
.startArrayField("array")
.add(1)
.add(2)
.add(3)
.end()
.startObjectField("object")
.put("name", "name3")
.put("age", 13)
.end()
.put("last", true)
.end()
.finish();
}
Java
运行上面的程序后,程序将会输出:
{
"objectArray" : [ {
"name" : "name1",
"age" : 11
}, {
"name" : "name2",
"age" : 12
} ],
"array" : [ 1, 2, 3 ],
"object" : {
"name" : "name3",
"age" : 13
},
"last" : true
}
Plain text
序列化和反序列化
Jackson-jr 可以让我们非常容易的把 Java 对象进行序列化,同时也能够非常容易的让我们从 JSON 字符串中读取为 Java 对象。
在对对象进行读写的时候,我们也可以对读写的属性进行定义,例如对输出的文本进行格式化或者使用自定义的日期属性进行输出,然后再从 Java 对象中写入到 String 字符串。
Jackson-jr 同时也能够支持复杂的数据结构,例如对象嵌套和数组等。通过对我们对象的关联关系的定义,我们可以让 JSON 字符串在进行反序列化序列化的时候完成对象映射,对于 Java 到 String 字符串的输出,其实也是一样。
// Serialization
String json = JSON.std.with(JSON.Feature.PRETTY_PRINT_OUTPUT)
.asString(person);
// Deserialization
json = "{\"name\":\"John Doe\",\"age\":30}";
Person person1 = JSON.std.with(JSON.Feature.PRETTY_PRINT_OUTPUT)
.beanFrom(Person.class, json);
Java
Jackson-jr 自定义选项
Jackson-jr 同时也支持大部分的注解(annotations)例如: @JsonProperty 这个和你日常使用的 Jackson 处理包是一样的。
你可以通过对对象注解的定义来控制序列化和反序列化的过程和数据。
对于可以支持的注解,可以范围 Jackson-jr 官方 GitHub 页面中有关的说明,这里我们就不一一列出,如果你用的是 Jackson 来处理序列化和反序列化的话,通常这些常用的注解应该还是比较熟悉的。
对于我们使用的序列化和反序列化输出来说,我们可以自定义输出,空对象如何输出等情况,这个和 Jackson 使用的方法是一致的。
JSON jsonMapper = JSON.std.with(JSON.Feature.PRETTY_PRINT_OUTPUT)
.with(JSON.Feature.WRITE_NULL_PROPERTIES)
.with(JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS);
String json = jsonMapper.asString(person);
Java
Jackson-jr 也允许我们创建自定义的序列化和反序列化来处理特定的数据类型和复杂的序列化方法。
通过对接口的实现, ValueWriter 和提供的继承的类, ValueReader 和 ReadWriterProvider,我们可以自定义和序列化和反序列化的方式。
Jackson-jr 现在还不能支持 java.time.*
,但是我们还是可以添加自定义的序列化和反序列化,如下面我们使用的代码:
public class CustomDateSerializer implements ValueWriter {
@Override
public void writeValue (JSONWriter jsonWriter, JsonGenerator jsonGenerator, Object o) throws IOException {
jsonGenerator.writeString(o.toString());
}
@Override
public Class<?> valueType () {
return LocalDate.class;
}
}
Java
public class CustomDateDeserializer extends ValueReader {
private final static DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd");
public CustomDateDeserializer () {
super(LocalDate.class);
}
@Override
public Object read (JSONReader jsonReader, JsonParser jsonParser) throws IOException {
return LocalDate.parse(jsonParser.getText(), dtf);
}
}
Java
当我们把我们自定义的代码注册到 JSON 处理对象后,我们就可以使用了 LocalDate 对象。
public class MyHandlerProvider extends ReaderWriterProvider {
@Override
public ValueWriter findValueWriter (JSONWriter writeContext, Class<?> type) {
if (type == LocalDate.class) {
return new CustomDateSerializer();
}
return null;
}
@Override
public ValueReader findValueReader (JSONReader readContext, Class<?> type) {
if (type.equals(LocalDate.class)) {
return new CustomDateDeserializer();
}
return null;
}
}
Java
上面和下面的这些代码显示了如何我们如何使用自定义的序列化和反序列化方法。
Person person = new Person("John Doe", 30, LocalDate.now());
JSON jsonMapper = JSON.builder().register(new JacksonJrExtension() {
@Override
protected void register (ExtensionContext extensionContext) {
extensionContext.insertProvider(new MyHandlerProvider());
}
}).build().with(JSON.Feature.PRETTY_PRINT_OUTPUT);
String json = jsonMapper.asString(person);
Person deserializedPerson = jsonMapper.beanFrom(Person.class, json);
Java
Jackson-jr 对比 Jackson
关于Jackson-jr 对比 Jackson 的内容,有人在做了一张下面的图。
简单点来说就 Jackson-jr 是Jackson 的轻量级应用,因为我们在很多时候都用不到 Jackson 的很多复杂功能。
对很多应用来说,我们可能只需要使用简单的 JSON 读写即可。
如我们用不到什么复杂的功能,并且使用了 Jackson-jr 能够满足你的项目使用的话,就直接使用 Jackson-jr 即可。
如发现 Jackson-jr 没有办法满足你的所有需求的时候,可以再切换到传统的 Jackson 包。
结论
Jackson-jr 提供了相对 Jackson 来说更加轻量的 JSON 数据处理模块。
对于很多只需要简单读写功能的 JSON 应用来说,就可以直接进行使用,对于服务器的应用,可能这个功能的削减对你的影响不大。
对大部分程序员来说,只需要了解 Jackson-jr 提供了一个轻量的兼容 JSON 大部分功能的解决方案即可。