一.简介
Jackson具有比较高的序列化和反序列化效率,据测试,无论是哪种形式的转换,Jackson > Gson > Json-lib,而且Jackson的处理能力甚至高出Json-lib近10倍左右,且正确性也十分高。相比之下,Json-lib似乎已经停止更新,最新的版本也是基于JDK15,而Jackson的社区则较为活跃。
下面,结合实例来对Jackson的用法进行简单介绍。
二.使用
Jackson提供了很多类和方法,而在序列化和反序列化中使用的最多的类则是ObjectMapper这个类,此类比较类似于Json-lib中JsonObject和ArrayObject。此类中提供了readTree(),readValue(),writeValueAsString()等方法用于转换。具体关于此类的说明文档地址是: http://jackson.codehaus.org/1.7.9/javadoc/org/codehaus/jackson/map/ObjectMapper.html。
为了避免重复描述,下面中所涉及到的objectMapper均是来至于ObjectMapper objectMapper = new ObjectMapper()。下面将按照序列化和反序列化两个方面来简单介绍用法。
1.序列化
1).对java自带类进行序列化
/*
* 对java自带类进行序列化
*/
private static void processList() {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
String teststringlist;
try {
teststringlist = objectMapper.writeValueAsString(list);
System.out.println(teststringlist);//[1,2,3]
} catch (Exception e) {
e.printStackTrace();
}
}
Jackson对一般类型的序列化是能简单实现的。
2).对自定义类的序列化
package com.bijian.study.dto;
import java.util.Date;
public class Student {
private int age = 10;
private String name = "hhh";
private String birthday;
public String[] list = {"hao", "haouhao", "keyi"};
public Date time = new Date();
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
}
为使例子更具有通用性,此类中包含了值类型int,引用类型String,String[],还包含了日期类型Date。
/*
* 对自定义类的序列化
*/
private static void processStudent() {
Student st = new Student();
try {
String teststringstu = objectMapper.writeValueAsString(st);
System.out.println(teststringstu);//{"age":10,"name":"hhh","list":["hao","haouhao","keyi"],"time":1458213712095}
} catch (Exception e) {
e.printStackTrace();
}
}
通过输出,可见转换得到的Json串是符合格式的。但是,时间的表示有点不符合标准。下面将介绍对时间格式的修改。
3).对时间格式的定义
Jackson有自己的默认时间格式,即timestamps形式,其效果即如上结果所显示的(例如:1375429228382)。如果想设置此格式是无效,通过objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false)便可设置,这样将使时间生成使用所谓的使用 [ISO-8601 ]-compliant notation, 输出类似如下格式的时间: "1970-01-01T00:00:00.000+0000"。
当然,也可以自定义输出的时间格式。
自定义时间格式的实现,例子还采用上面所介绍的student类。
/*
* 自定义时间格式的实现
*/
private static void processDateFormat() {
Student st = new Student();
java.text.DateFormat myFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
objectMapper.getSerializationConfig().setDateFormat(myFormat);
try {
String teststringstu = objectMapper.writeValueAsString(st);//{"age":10,"name":"hhh","list":["hao","haouhao","keyi"],"time":"2016-03-17 19:35:09"}
System.out.println(teststringstu);
} catch (Exception e) {
e.printStackTrace();
}
}
可见时间输出格式变成了我们想要的了。在Jackson中定义时间输出格式的方法比在Json-lib中对时间格式的定义简便很多。
4).另一种序列化方法
/*
* 另一种序列化方法
*/
private static void processStudent2() {
Student st=new Student();
JsonGenerator jsonGenerator;
try {
jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(System.out, JsonEncoding.UTF8);//{"age":10,"name":"hhh","list":["hao","haouhao","keyi"],"time":"2016-03-17 19:38:06"}
jsonGenerator.writeObject(st);
System.out.println();//仅用于换行
} catch (IOException e) {
e.printStackTrace();
}
}
此方法同样可以得到上面方法的值。但是注意此方法中的这个函数:createJsonGenerator(),它需要两个参数,一个是OutputStream类型参数,一个是JsonEncoding类型参数。通过这两个参数,我们可以了解到,此方法不仅可以将Json直接写入网络流,还可以将Json写入文件流或者内存流。所以用途更广。
2.反序列化
1).一次性反序列化
此方法中主要利用ObjectMapper提供的<testJsonClass> readValue(String content, Class<testJsonClass> valueType)方法。此方法需要输入Json串以及对应的需要填充的类的Class,返回填充后的类。
a.将Json串解析到自定义类中
package com.bijian.study.dto;
import java.util.ArrayList;
import java.util.List;
public class TestJsonClass {
public int objectID;
public List geoPoints=new ArrayList();
}
/*
* 将Json串解析到自定义类中
*/
private static void processJsonToTestJsonClass() {
String test1 = "{\"objectID\":357,\"geoPoints\":[{\"x\":504604.59802246094,\"y\":305569.9150390625}]}";
TestJsonClass testClass;
try {
testClass = objectMapper.readValue(test1, TestJsonClass.class);
System.out.println(testClass.objectID);//357
System.out.println(testClass.geoPoints);//[{x=504604.59802246094, y=305569.9150390625}]
} catch (Exception e) {
e.printStackTrace();
}
}
b.将Json串反序列化到系统自带的类中
/*
* 将Json串反序列化到系统自带的类中
*/
private static void processJsonToMap() {
String json = "{\"error\":0,\"data\":{\"name\":\"ABC\",\"age\":20,\"phone\":{\"home\":\"abc\",\"mobile\":\"def\"},\"friends\":[{\"name\":\"DEF\",\"phone\":{\"home\":\"hij\",\"mobile\":\"klm\"}},{\"name\":\"GHI\",\"phone\":{\"home\":\"nop\",\"mobile\":\"qrs\"}}]},\"other\":{\"nickname\":[]}}";
try {
Map<String, Map<String, Object>> maps = objectMapper.readValue(json, Map.class);
System.out.println(maps.get("error"));//0
System.out.println((Object) (maps.get("data").get("phone")));//{home=abc, mobile=def}
} catch (Exception e) {
e.printStackTrace();
}
}
用系统自带的Map定义一个变量:Map<String, Map<String, Object>> maps。然后利用maps = objectMapper.readValue(json, Map.class)便可将Json反序列化到变量maps中。
通过System.out.println(maps.get("error"));System.out.println((Object)(maps.get("data").get("phone"))),可在控制台中得到结果。
2).渐次反序列化
此方法更灵活,可以只将用户感兴趣的Json串信息值提取出来。主要利用ObjectMapper提供的readTree和Jackson提供的JsonNode类来实现。
/*
* 渐次反序列化
*
* 运行结果:
* 357
* 504604.59802246094 305569.9150390625
* 358
* 504602.2680053711 305554.43603515625
*/
private static void processJsonToJsonNode() {
String test = "{\"results\":[{\"objectID\":357,\"geoPoints\":[{\"x\":504604.59802246094,\"y\":305569.9150390625}]},{\"objectID\":358,\"geoPoints\":[{\"x\":504602.2680053711,\"y\":305554.43603515625}]}]}";
try {
JsonNode node = objectMapper.readTree(test);
//将Json串以树状结构读入内存
JsonNode contents = node.get("results");//得到results这个节点下的信息
for (int i = 0; i < contents.size(); i++) { //遍历results下的信息,size()函数可以得节点所包含的的信息的个数,类似于数组的长度
System.out.println(contents.get(i).get("objectID").getIntValue()); //读取节点下的某个子节点的值
JsonNode geoNumber = contents.get(i).get("geoPoints");
for (int j = 0; j < geoNumber.size(); j++) { //循环遍历子节点下的信息
System.out.println(geoNumber.get(j).get("x").getDoubleValue() + " "
+ geoNumber.get(j).get("y").getDoubleValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
此Json串比较复杂,包含了嵌套数组的形式,具有通用性。
此方法类似于XML解析中的DOM方式解析,其好处是结构明细,便于提取想要的信息。当然,其缺点也和此方法一样:耗时费空间。
三.总结
Jackson关于Json的操作主要如上所示,其方法使用起来很便利,而且也很灵活,即提供了一次性完成的操作,也提供了可以按需读取信息的操作。并且Jackson的功能很齐全,可以对序列化和反序列化进行多种细节的控制,例如注解功能和对于Hibernate的延迟注入功能以及设置时间格式功能等,因为这些功能目前不太需要,所以仔细研究留待以后。同时,Jackson还支持对XML的一系列序列化和反序列化的操作,其思路与解析Json的大致相同。
对于Jackson目前的缺点,网上有人测试所比Json-lib更占内存一些。而利用空间换时间,一般是值得的。
PS:上篇序列化反序列化和这篇的完整代码实例见附件