Jackson使用总结

JSON(JavaScript Object Notation),是一种简单易读的数据交换格式。 它在全球无数项目中很受欢迎并且实现,对于那些不喜欢XML的人来说,JSON是一个非常好的替代解决方案。

处理JSON的java工具很多,我们关注比较流行的Jackson、Google Gson和fastjson。

Jackson

Jackson是一个简单基于Java应用库,Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象。Jackson所依赖的jar包较少,简单易用并且性能也要相对高些,并且Jackson社区相对比较活跃,更新速度也比较快。

Jackson有以下特点:

  • 容易使用, jackson API提供了一个高层次外观,以简化常用的用例。
  • 无需创建映射, API提供了默认的映射大部分对象序列化。
  • 性能高 ,快速,低内存占用,适合大型对象图表或系统。
  • 干净的JSON , jackson创建一个干净和紧凑的JSON结果,这是让人很容易阅读。
  • 不依赖 , 库不需要任何其他的库,除了JDK。
  • 开源代码, jackson是开源的,可以免费使用。

JSON 提供了三种不同的方法来处理

  1. JSON 流式API , 读取并将JSON内容写入作为离散事件。 JsonParser读取数据,而JsonGenerator写入数据。它是三者中最有效的方法,是最低的开销和最快的读/写操作。它类似于Stax解析器XML。
  2. 树模型 ,准备JSON文件在内存里以树形式表示。 ObjectMapper构建JsonNode节点树。这是最灵活的方法。它类似于XML的DOM解析器。
  3. 数据绑定, 转换JSON并从POJO(普通Java对象)使用属性访问或使用注释。它有两个类型。
  • 简单的数据绑定 ,转换JSON和Java Maps, Lists, Strings, Numbers, Booleans 和null 对象。
  • 全部数据绑定 , 转换为JSON从任何JAVA类型。
  1. ObjectMapper读/写JSON两种类型的数据绑定。数据绑定是最方便的方式是类似XML的JAXB解析器。

jackson包介绍

  • jackson-core,核心包,提供基于”流模式”解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。

  • jackson-annotations,注解包,提供标准注解功能;

  • jackson-databind ,数据绑定包, 提供基于”对象绑定” 解析的相关 API ( ObjectMapper ) 和”树模型” 解析的相关 API (JsonNode);基于”对象绑定” 解析的 API 和”树模型”解析的 API 依赖基于”流模式”解析的 API。

Gson

Gson(又称Google Gson)是Google公司发布的一个开放源代码的Java库,主要用途为序列化Java对象为JSON字符串,或反序列化JSON字符串成Java对象。

Gson的应用主要为toJson与fromJson两个转换函数,而在使用这种对象转换之前需先创建好对象的类别以及其成员才能成功的将JSON字符串成功转换成相对应的对象。

1
2
3
4
5
6
class User {
private age = 5;
private String username = "paopaozhu";
User(){
} // default constructor
}

序列化JAVA对象成JSON字符串

1
2
3
User user = new User();
Gson gson = new Gson();
String json = gson.toJson(user);

反序列化JSON字符串成对应的JAVA对象

1
User user= gson.fromJson(json,User.class);

使用方式

使用Jackson将java对象和json对象相互转换非常简单。

关键技术 1 引用依赖

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>

jackson-databind 依赖 jackson-core 和 jackson-annotations,当添加 jackson-databind 之后, jackson-core 和 jackson-annotations 也随之添加到 Java 项目工程中。在添加相关依赖包之后,就可以使用 Jackson。

关键技术 2 使用 ObjectMapper 对象的方法写成String、写成文件

1
2
3
String writeValueAsString(Object value) 写String

void writeValue(File resultFile, Object value) 写文件

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
package com.wukong.learn.jackson;

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;

/**
* POJO类,用于序列化和反序列化
*
* @author paopaozhu
*
*/
public class User {

private String name;

private Integer age;

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date birthday;

private String email;

public User() {
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", birthday=" + birthday + ", email=" + email + "]";
}

}

@JsonFormat 此注解用于属性上,作用是把Date类型直接转化为想要的格式,如@JsonFormat(pattern = “yyyy-MM-dd HH-mm-ss”)。

@JsonIgnore 此注解用于属性上,作用是进行JSON操作时忽略该属性。不会输出被注释的字段和值。

@JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把Nickname属性序列化为name,@JsonProperty(“name”)。

Java对象转换成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
32
33
34
35
package com.wukong.learn.jackson;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.text.SimpleDateFormat;
import java.io.IOException;
import java.text.ParseException;

public class ToJson {

public static void main(String[] args) {
User user = new User();
user.setName("paopaozhu");
user.setEmail("admin@lpaopaozhu.com");
user.setAge(4);
SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");

try {
user.setBirthday(dateformat.parse("2020-05-02"));
} catch (ParseException e) {
e.printStackTrace();
}

ObjectMapper mapper = new ObjectMapper();

try {
String json = mapper.writeValueAsString(user);
System.out.printf("json="+json);
//mapper.writeValue(new File("/Users/zyy/Downloads/user.json"),user);
}catch (IOException e){
e.printStackTrace();
}

}
}

输出结果

1
json={"name":"paopaozhu","age":4,"birthday":"2020年05月01日","email":"admin@lpaopaozhu.com"}

将json对象转换成java对象

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
package com.wukong.learn.jackson;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class FromJson {

public static void main(String[] args) {
String json = "{\"name\":\"paopaozhu\",\"age\":4,\"birthday\":\"2020-05-02 00:00:00\"," +
"\"email\":\"admin@lpaopaozhu.com\"}";

ObjectMapper mapper = new ObjectMapper();
User user = null;
try {
user = mapper.readValue(json, User.class);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(user);
}
}

List/Java对象互转

前端发给后台的数据是一个数组对象,数组里的元素也是对象。

它是下面格式的数据:

1
[{"id":1,"checked":true},{"id":3,"checked":true},{"id":4,"checked":true},{"id":2,"checked":false}]

在Jackson的基础上, 我们需要一个DTO对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.wukong.learn.jackson;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PermissionChecker {
private int id;
private boolean checked;
}

这个DTO对象有id和checked两个属性,用于对应json中的id和checked。

接下来就使用Jackson 工具将接收到的json转换成List<PermissionChecker >

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
package com.wukong.learn.jackson;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;

public class ListToJson {
public static void main(String[] args) {
String jsonstr="[{\"id\":1,\"checked\":true},{\"id\":3,\"checked\":true}," +
"{\"id\":4,\"checked\":true},{\"id\":2,\"checked\":false}]";
ObjectMapper mapper = new ObjectMapper();
JavaType jt = mapper.getTypeFactory().constructParametricType(ArrayList.class, PermissionChecker.class);
List<PermissionChecker> list= new ArrayList<PermissionChecker>();
try {
list = mapper.readValue(jsonstr, jt);
}catch(Exception e){
e.printStackTrace();
}
for(PermissionChecker pc:list) {
System.out.println(pc.getId()+" is "+pc.isChecked());
}
}
}

Map/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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.wukong.learn.jackson;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MapToJson {
public static void main(String[] args) {
//toJson();
toMap();
}

private static void toJson() {
Map<String,Object> map = new HashMap<String,Object>();
map.put("site", "昨非");
map.put("URL", "https://alfredliukai.github.io");

ObjectMapper mapper = new ObjectMapper();
String json;
try{
json = mapper.writeValueAsString(map);
System.out.println(json);
}catch (JsonProcessingException e){
e.printStackTrace();
}
}

private static void toMap(){
ObjectMapper mapper = new ObjectMapper();

String json = "{\"site\":\"昨非\",\"URL\":\"https://alfredliukai.github.io\"}";

Map<String, Object> map = new HashMap<String, Object>();

try{
map= mapper.readValue(json, new TypeReference<Map<String,Object>>() {});
}catch (JsonParseException e){
e.printStackTrace();
}catch (JsonMappingException e){
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
for (Map.Entry<String,Object> entry : map.entrySet()){
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}

Tree Model

树形结构的数据在工作中出现的频率非常高。

如一个叫张大大的同学,他的信息用json表示如下:

1
2
3
4
5
6
7
8
9
10
11
{
"id" : 1,
"name" : {
"first" : "zhang",
"last" : "dada"
},
"contact" : [
{ "type" : "QQ", "ref" : "88888888"},
{ "type" : "phone", "ref" : "18822222222"}
]
}

这样数据具有树形的特点,处理方式有些繁琐。

处理单条记录

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
67
68
69
package com.wukong.learn.jackson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class TreeModel {
public static void main(String[] args) {
String content = "{\n" +
" \"id\" : 1,\n" +
" \"name\" : {\n" +
" \"first\" : \"zhang\",\n" +
" \"last\" : \"dada\"\n" +
" },\n" +
" \"contact\" : [\n" +
" { \"type\" : \"QQ\", \"ref\" : \"88888888\"},\n" +
" { \"type\" : \"phone\", \"ref\" : \"18822222222\"}\n" +
" ]\n" +
"}";

long id;
String firstName = "";
String lastName = "";

ObjectMapper mapper = new ObjectMapper();
JsonNode root;

try {
root = mapper.readTree(content);

id = root.path("id").asLong();
System.out.println("id: "+id);

JsonNode nameNode = root.path("name");
if (nameNode.isMissingNode()) {
// 处理缺失的节点
} else {

firstName = nameNode.path("first").asText();
lastName = nameNode.path("last").asText();

System.out.println("姓: " + firstName);
System.out.println("名: " + lastName);

}

JsonNode contactNode = root.path("contact");
if (contactNode.isArray()) {
// 处理数组
}

for (JsonNode node : contactNode) {
String type = node.path("type").asText();
String ref = node.path("ref").asText();
System.out.println("type : " + type);
System.out.println("ref : " + ref);
}

System.out.println("---------------------- 我是分割线 ------------------------");

} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

关键技术:

使用 readTree(File file) 方法从磁盘读取json文件
使用 JsonNode 对象的 path(String fieldName); 方法返回一个JsonNode
再对JsonNode 对象进行类型转换,转换成基本的数据类型或包装器类型

计算结果:

1
2
3
4
5
6
7
8
id: 1
姓: zhang
名: dada
type : QQ
ref : 88888888
type : phone
ref : 18822222222
---------------------- 我是分割线 ------------------------

流式API

使用Jackson流式API可以读写json

用到的关键技术:

  • com.fasterxml.jackson.core.JsonGenerator 写入json文件
  • com.fasterxml.jackson.core.JsonParser 解析文件
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
package com.wukong.learn.jackson;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;

import java.io.File;
import java.io.IOException;

public class APIReadAndWrite {
public static void main(String[] args) {
JsonFactory factory = new JsonFactory();

try {
JsonGenerator generator = factory.createGenerator(new File("/Users/zyy/Downloads/user.json"),
JsonEncoding.UTF8);

generator.writeStartObject();

generator.writeStringField("site", "alfred.github.io");
generator.writeStringField("URL", "http://alfred.github.io");

generator.writeFieldName("messages");
generator.writeStartArray(); // [

generator.writeString("第一条消息 为人民服务");
generator.writeString("第二条消息 好好学习,天天向上");
generator.writeString("第三条消息 快乐编程");

generator.writeEndArray(); // ]

generator.writeEndObject(); // }

generator.close();
} catch (IOException e) {

e.printStackTrace();
}
}

}

关键方法有:

  • JsonFactory 对象用于产生JsonGenerator 对象;
  • void writeStartObject() 方法用于写一个{
  • void writeEndObject() 方法用于写一个}
  • void writeStartArray() 方法用于写入一个[
  • void writeEndArray() 方法用于写一个]
  • void writeStringField(String fieldName, String value) 方法用于写入一个字段,包括字段名和值,字段是String类型
  • void writeNumberField(String fieldName, int value) 方法用于写入一个字段,包括字段名和值,字段是Number类型
  • void writeString(String text) 方法用于写入一个值

计算结果:

1
{"site":"alfred.github.io","URL":"http://alfred.github.io","messages":["第一条消息 为人民服务","第二条消息 好好学习,天天向上","第三条消息 快乐编程"]}

JsonParser解析文件
使用JsonParser可以解析json文件。这种解析试建立在对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
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.wukong.learn.jackson;

import com.fasterxml.jackson.core.*;

import java.io.File;
import java.io.IOException;

public class APIReadAndWrite {
public static void main(String[] args) {
//jsonGenerator();
jsonParser();
}

private static void jsonGenerator() {
JsonFactory factory = new JsonFactory();

try {
JsonGenerator generator = factory.createGenerator(new File("/Users/zyy/Downloads/user.json"),
JsonEncoding.UTF8);

generator.writeStartObject();

generator.writeStringField("site", "alfred.github.io");
generator.writeStringField("URL", "http://alfred.github.io");

generator.writeFieldName("messages");
generator.writeStartArray(); // [

generator.writeString("第一条消息 为人民服务");
generator.writeString("第二条消息 好好学习,天天向上");
generator.writeString("第三条消息 快乐编程");

generator.writeEndArray(); // ]

generator.writeEndObject(); // }

generator.close();
} catch (IOException e) {

e.printStackTrace();
}
}

private static void jsonParser(){
JsonFactory factory = new JsonFactory();

try {
JsonParser parser = factory.createParser(new File("/Users/zyy/Downloads/user.json"));

// 读取每一行,直到找到 "}"
while (parser.nextToken() != JsonToken.END_OBJECT) {

String fieldname = parser.getCurrentName();
if ("site".equals(fieldname)) {
// 找到site后,再找下一个
parser.nextToken();
System.out.println(parser.getText());

}

if ("URL".equals(fieldname)) {
// 找到URL后,再找下一个
parser.nextToken();
System.out.println(parser.getText());

}

if ("messages".equals(fieldname)) {
parser.nextToken();
// 读取每一行,直到找到 "]"
while (parser.nextToken() != JsonToken.END_ARRAY) {
System.out.println(parser.getText());
}
}
}
parser.close();

} catch (JsonParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

}

关键方法:

JsonToken nextToken() 方法用于读取每一个JsonToken
JsonParser 对象的方法 String getCurrentName() 读取字段名
JsonParser 对象的方法 String getText() 读取字段值

参考文献

  1. https://www.jianshu.com/p/040e8671e560
  2. https://www.ibm.com/developerworks/cn/java/jackson-advanced-application/index.html
  3. https://www.wangshenghua.com/wiki/jackson/f5789c8c7cd39ad348e3a8ccadfa9523/