Jackson 笔记

  1. Data Binding
    1. 序列化
    2. 数据绑定
    3. 泛型绑定
    4. 数组绑定
    5. 线程安全
  2. Tree Model
  3. Streaming API
  4. SerializationFeature
    1. WRAP_ROOT_VALUE
    2. INDENT_OUTPUT
    3. WRITE_DATES_AS_TIMESTAMPS
    4. WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS
    5. ORDER_MAP_ENTRIES_BY_KEYS
  5. DeserializationFeature
    1. FAIL_ON_UNKNOWN_PROPERTIES
  6. MapperFeature
    1. ACCEPT_CASE_INSENSITIVE_PROPERTIES
  7. 注解
    1. JsonProperty
    2. JsonInclude
    3. JsonSerialize
  8. module

Jackson 提供了三种对JSON处理的方式
  • Data Binding
  • Tree Model
  • Streaming API

Data Binding

这种方式提供了JSON数据和Java对象之间的无缝转换,而且这种方式是相当便利的. 它内部基于 Streaming API 的JSON 读写系统, 尽管Data Binding 是非常高效地,但是相比纯 streaming/incremental 方式,仍然有一些额外的性能消耗.

序列化

1
2
3
4
5
6
7
8
9
10
11
   @Test
public void test_Serialization() throws IOException {
Obj obj = new Obj();
obj.platform = "qq";
StringWriter stringWriter = new StringWriter();

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(stringWriter, obj);

System.out.println(stringWriter);
}

输出为{"platform":"qq"}

数据绑定

1
2
3
4
5
6
7
@Test
public void test_ObjectMapperRead () throws IOException {
String jsonStr = "{\"platform\":\"1\"}";
ObjectMapper objectMapper = new ObjectMapper();
Obj obj = objectMapper.readValue(jsonStr, Obj.class);
System.out.println(obj.platform);
}

输出为1

泛型绑定

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test_Serialization() throws IOException {
Map<Integer, String> map = new HashMap<>();
map.put(2016, "10/11");
StringWriter stringWriter = new StringWriter();

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(stringWriter, map);

Map<Integer, String> newMap = objectMapper.readValue(stringWriter.toString(), new TypeReference<Map<Integer, String>>() {});
System.out.println(newMap.get(2016));
}

输出为10/11

数组绑定

1
2
3
4
5
6
7
8
9
10
11
@Test
public void test_Binding() throws IOException {
String[] array = {"2016"};
StringWriter stringWriter = new StringWriter();

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(stringWriter, array);

String[] arr = objectMapper.readValue(stringWriter.toString(), String[].class);
System.out.println(arr[0]);
}

线程安全

ObjectMapper被共享出来之后, 只要重新配置共享实例, 那么它就是线程安全的. 也就是不要调用下列方法

  • enable()
  • disable()
  • configure()

参考

Tree Model

Tree Model 和XML 处理方式 非常类似.

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
34
35
36
37
38
39
40
41
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

public class TestTreeModel {

@Test
public void test_() throws Exception {
Obj1 obj1 = new Obj1();
Obj2 obj2 = new Obj2();
Obj3 obj3 = new Obj3();
obj1.obj2 = obj2;
obj2.obj3 = obj3;
obj3.string = "hello";

ObjectMapper objectMapper = new ObjectMapper();
String str = objectMapper.writeValueAsString(obj1);
JsonNode objtree = objectMapper.readTree(str);
System.out.println("get obj2 : " + objtree.get("obj2"));
System.out.println("get obj3 : " + objtree.get("obj3"));
System.out.println("get string : " + objtree.get("string"));

System.out.println("path obj2 : " + objtree.path("obj2"));
System.out.println("path obj3 : " + objtree.path("obj3"));
System.out.println("path string : " + objtree.path("string"));

System.out.println("path string obj2 -> obj3 -> string : " + objtree.path("obj2").path("obj3").path("string"));
}

private static class Obj1 {
public Obj2 obj2;
}

private static class Obj2 {
public Obj3 obj3;
}

private static class Obj3 {
public String string;
}
}

输出结果为

1
2
3
4
5
6
7
get obj2 : {"obj3":{"string":"hello"}}
get obj3 : null
get string : null
path obj2 : {"obj3":{"string":"hello"}}
path obj3 :
path string :
path string obj2 -> obj3 -> string : "hello"

当调用get()方法时, 如果找不到的话, 会返回null, 而path()找不到的话则会返回MissingNode

还有一个方法with(int index)我们没有演示, 这个方法是如果找不到就添加.

get()方法还有一个特别有用的地方就是用来处理数组.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

public class TestTreeModel {

@Test
public void test_() throws Exception {
Obj1 obj1 = new Obj1();

ObjectMapper objectMapper = new ObjectMapper();
String str = objectMapper.writeValueAsString(obj1);
System.out.println(str);

JsonNode tree = objectMapper.readTree(str);
System.out.println(tree.get("strings").get(1));
}

private static class Obj1 {
public String[] strings = {"123", "456"};
}
}

结果为

1
2
{"strings":["123","456"]}
"456"

Streaming API

Streaming API 是 Jackson处理 JSON最高效地方式. 但是它的易用性却大大地降低了, 我们不能像Data Binding 或者 Tree Model 那样随机访问元素.

SerializationFeature

WRAP_ROOT_VALUE

是否环绕根元素,默认false,如果为true,则默认以类名作为根元素,也可以通过@JsonRootName来自定义根元素名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.junit.Test;

import java.io.IOException;

public class TestDataBinding {

@Test
public void test_SerializationFeature () throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
System.out.println( objectMapper.writeValueAsString(new Obj()));
}

public static class Obj {
public String platform = "example";
}
}

结果为{"Obj":{"platform":"example"}} 如果不开启WRAP_ROOT_VALUE的话, 结果为{"platform":"example"}

INDENT_OUTPUT

是否缩放排列输出

1
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

结果为

1
2
3
{
"platform" : "example"
}

WRITE_DATES_AS_TIMESTAMPS

序列化日期时以timestamps输出

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestDataBinding {

@Test
public void test_SerializationFeature () throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
System.out.println( objectMapper.writeValueAsString(new Obj()));
}

public static class Obj {
public Date now = new Date();
}
}

结果为

1
{"now":1476179780913}

WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS

序列化char[]时以json数组输出

ORDER_MAP_ENTRIES_BY_KEYS

序列化Map时对key进行排序操作

DeserializationFeature

FAIL_ON_UNKNOWN_PROPERTIES

在反序列化时, 如果Java对象中不包含json串的某个数据 属性, 则会报错.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestDataBinding {

@Test
public void test_SerializationFeature () throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

String str = "{\"strings1\":[\"123\"],\"strings2\":[\"456\"]}";
objectMapper.readValue(str, Obj.class);
}

public static class Obj {
public String[] strings1 = {"123"};
}
}

MapperFeature

ACCEPT_CASE_INSENSITIVE_PROPERTIES

在反序列化时是否忽略大小写

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
34
35
36
37
38
39
40
41
42
43
44
45
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

import java.io.IOException;

public class TestDataBinding {

@Test
public void test_SerializationFeature () throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Upper upper = new Upper();
upper.setFirstName("John");
System.out.println(objectMapper.writeValueAsString(upper));

Lower lower = new Lower();
lower.setLastName("li");
System.out.println(objectMapper.writeValueAsString(lower));

}

@Test
public void test_DeserializationFeature () throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);

Upper obj1 = objectMapper.readValue("{\"firstName\":\"John\"}", Upper.class);
System.out.println(obj1.getFirstName());

Upper obj2 = objectMapper.readValue("{\"FirstName\":\"John\"}", Upper.class);
System.out.println(obj2.getFirstName());
}
}

class Upper {
private String FirstName;
public String getFirstName() {return FirstName;}
public void setFirstName(String firstName) {FirstName = firstName;}
}

class Lower {
private String lastName;
public String getLastName() {return lastName;}
public void setLastName(String lastName) {this.lastName = lastName;}
}

test_SerializationFeature()这个测试中, 我们可以通过结果看到, Upper的FirstName在JSON串 中成了firstName. 当反序列化得时候, 如果不指定ACCEPT_CASE_INSENSITIVE_PROPERTIES, 那么当JSON串中的FirstName为大写的时候, 是没办法序列化出来的.

注解

JsonProperty

jackson 默认是根据Getter来进行注值反序列化的, 但是有时候为了节省存储空间, 字段名远小于Getter名称, 这样就造成了字段名和方法名不一致, 此时就可以利用JsonProperty注解重命名来解决这个问题

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
34
35
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

import java.io.IOException;

public class TestDataBinding {

@Test
public void test_Deferent () throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);

Different different = new Different();
different.setMiddle("Nice");
System.out.println(objectMapper.writeValueAsString(different));

Different obj2 = objectMapper.readValue("{\"middle\":\"Nice\"}", Different.class);
System.out.println(obj2.getMiddle());

Different obj3 = objectMapper.readValue("{\"Mid\":\"Nice\"}", Different.class);
System.out.println(obj3.getMiddle());

Different obj4 = objectMapper.readValue("{\"mid\":\"Nice\"}", Different.class);
System.out.println(obj4.getMiddle());
}
}

class Different {
@JsonProperty("mid")
private String Mid;
public String getMiddle() {return Mid;}
public void setMiddle(String middle) {this.Mid = middle;}
}

说到这里就需要点出一个坑了

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
34
35
36
37
38
39
40
41
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;

import java.io.IOException;

public class TestPrivate {

public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();

SimpleId simpleId = new SimpleId();
simpleId.setString("simple Id");
String json = objectMapper.writeValueAsString(simpleId);
System.out.println("Jsckson ---> " + json);

Gson gson = new Gson();
json = gson.toJson(simpleId);
System.out.println("Gson ---> " + json);

json = JSON.toJSONString(simpleId);
System.out.println("FastJson ---> " + json);
}

public static class SimpleId {
private String stringId;
private String stringName = "empty name";

public String getString() {
return stringId;
}

public void setString(String string) {
this.stringId = string;
}

public String getString1() {
return "123456";
}
}
}

结果为

1
2
3
Jsckson  ---> {"string":"simple Id","string1":"123456"}
Gson ---> {"stringId":"simple Id","stringName":"empty name"}
FastJson ---> {"string":"simple Id","string1":"123456"}

有时候我们需要Gson这种输出结果, 即不使用Getter, 而是使用filed进行序列化, 怎么办呢?我们可以使用 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE), 但是对于有时候我们并不想在每个类上面都加一个这样的注解, 配置一些ObjectMapper就可以了

1
2
3
4
5
6
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));

这样结果为

1
2
3
Jsckson  ---> {"stringId":"simple Id","stringName":"empty name"}
Gson ---> {"stringId":"simple Id","stringName":"empty name"}
FastJson ---> {"string":"simple Id","string1":"123456"}

JsonInclude

指定在序列化时, 可以输出哪些值. 例如只输出非默认值(包含类型默认值和初始化默认值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

public class TestDefault {
@Test
public void test_() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Foo foo = new Foo();
foo.string1 = "123";
System.out.println(objectMapper.writeValueAsString(foo));
}
}

@JsonInclude(JsonInclude.Include.NON_DEFAULT)
class Foo {
public String string1;
public String string2 = "xxx";
public String string3;
}

JsonSerialize

实现浮点数只保留俩位

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.junit.Test;

import java.io.IOException;
import java.text.DecimalFormat;

public class TestDouble {

@Test
public void test_() throws JsonProcessingException {
DoubleObject doubleObject = new DoubleObject();
doubleObject.aDouble = 1.101010101010101;
doubleObject.aFloat = 1.101010101010101f;

ObjectMapper objectMapper = new ObjectMapper();
String string = objectMapper.writeValueAsString(doubleObject);
System.out.println(string);
}

public static class CustomDoubleSerializer extends JsonSerializer<Number> {
@Override
public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (null == value) {
jgen.writeNull();
} else if (value instanceof Double || value instanceof Float){
final String pattern = ".##";
final DecimalFormat myFormatter = new DecimalFormat(pattern);
final String output = myFormatter.format(value);
jgen.writeNumber(output);
}
}
}

public static class DoubleObject {
@JsonSerialize(using = CustomDoubleSerializer.class)
private double aDouble;
@JsonSerialize(using = CustomDoubleSerializer.class)
private float aFloat;
public double getaDouble() {return aDouble;}
public void setaDouble(double aDouble) {this.aDouble = aDouble;}
public float getaFloat() {return aFloat;}
public void setaFloat(float aFloat) {this.aFloat = aFloat;}
}
}

module

可以通过module来自定义实现 序列化和反序列机制. 下面的例子中就是演示对Double类型的序列化时保留浮点数位数的实现

注意 如果注解和自定义序列化重复时, 那么注解的设置会覆盖自定义序列化机制. 而且对于原生类型来说, 是区分原生类型和包装类型的

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.IOException;
import java.text.DecimalFormat;

public class TestModule {

private static final ObjectMapper objectMapper;

static {
objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Double.class, new CustomFiveDoubleSerializer());
simpleModule.addSerializer(double.class, new CustomFiveDoubleSerializer());
objectMapper.registerModule(simpleModule);
}

public static class CustomFiveDoubleSerializer extends JsonSerializer<Number> {
@Override
public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (null == value) {
jgen.writeNull();
} else if (value instanceof Double || value instanceof Float){
final String pattern = ".#####";
final DecimalFormat myFormatter = new DecimalFormat(pattern);
final String output = myFormatter.format(value);
jgen.writeNumber(output);
}
}
}

public static class CustomOneDoubleSerializer extends JsonSerializer<Number> {
@Override
public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (null == value) {
jgen.writeNull();
} else if (value instanceof Double || value instanceof Float){
final String pattern = ".#";
final DecimalFormat myFormatter = new DecimalFormat(pattern);
final String output = myFormatter.format(value);
jgen.writeNumber(output);
}
}
}

public static void main(String[] args) throws JsonProcessingException {
Obj obj = new Obj();
obj.aDouble1 = 1.111111111111;
obj.aDouble2 = 1.111111111111;

System.out.println(objectMapper.writeValueAsString(obj));
}

public static class Obj {
public Double aDouble1;
@JsonSerialize(using = CustomOneDoubleSerializer.class)
public Double aDouble2;
}

}