概述
jackson的架构和开发思路是从java平台的STAX、JAXB等XML工具获得的灵感,同样继承了他们的高质量和高灵活性。同样,XML操作的DOM方式和STAX方式在jackson里面也能看到,分别对应的是TreeModel和StreamingAPI。jackson的野心巨大,希望能够给开发者带来快速、正确、轻量、顺手的使用体验。
接下来我们一起简略看一下jackson的整体能力:
JSON的三种使用方式
jackson提供了三种处理JSON的方式,其中Data binding方式有两个变种:
Streaming API方式(使用事件方式读写JSON内容):
使用org.codehaus.jackson.JsonParser读,使用org.codehaus.jackson.JsonGenerator写;
该方式灵感来源于XML的STAX 方式。
Tree Model (使用生成JSON文档内存树的方式,将JSON内容一次读入内存):
使用org.codehaus.jackson.map.ObjectMapper构建树,树中是JsonNode;
树模型类似于XML的DOM模型。
Data Binding(使用属性方法或者注解实现JSON和POJOs之间的相互转换):
提供了两种方式,一种是完全对象(full data binding)绑定,一种是简单对象(simple data binding)绑定。
简单对象绑定用来在JSON和java的Maps, Lists, Strings, Numbers, Booleans, and null之间转换;
完全对象绑定用来在JSON和java bean对象之间的转换,同样适用于java内置对象。
总结一下三种方法:
Streaming API性能最好(最低的消耗、最快的读写速度、其他两种方法的基础)
Data Binding 好用、便利
Tree Model 灵活
Full Data Binding (POJO) Example
org.codehaus.jackson.map.ObjectMapper 用来实现JSON和POJOs之间的转换。
比如有如下内容的json文件user.json:
{
"name" : { "first" : "Joe", "last" : "Sixpack" },
"gender" : "MALE",
"verified" : false,
"userImage" : "Rm9vYmFyIQ=="
}
只需要两行代码,就可以把上述json转成User示例:
ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
User user = mapper.readValue(new File("user.json"), User.class);
User对象定义是这样的:
public class User {
public enum Gender { MALE, FEMALE };
public static class Name {
private String _first, _last;
public String getFirst() { return _first; }
public String getLast() { return _last; }
public void setFirst(String s) { _first = s; }
public void setLast(String s) { _last = s; }
}
private Gender _gender;
private Name _name;
private boolean _isVerified;
private byte[] _userImage;
public Name getName() { return _name; }
public boolean isVerified() { return _isVerified; }
public Gender getGender() { return _gender; }
public byte[] getUserImage() { return _userImage; }
public void setName(Name n) { _name = n; }
public void setVerified(boolean b) { _isVerified = b; }
public void setGender(Gender g) { _gender = g; }
public void setUserImage(byte[] b) { _userImage = b; }
}
把User实例转回成json如下:
mapper.writeValue(new File("user-modified.json"), user);
Simple Data Binding Example
如果我们没有或者不想创建一个单独的类来做JSON和POJO之间的转换,那么可以使用这种方式来做。这种方式和Full Data Binding的方式实质上是一样的,差别只在于指定的POJO类型是java的内置类型(Object.class,Map.class, List.class, String[].class等)。用法和上边一样,举个例子:
JSON转换成java对象实例的方法:
Map<String,Object> userData = mapper.readValue(new File("user.json"), Map.class);
java对象实例转换成JSON的方法:
Map<String,Object> userData = new HashMap<String,Object>();
Map<String,String> nameStruct = new HashMap<String,String>();
nameStruct.put("first", "Joe");
nameStruct.put("last", "Sixpack");
userData.put("name", nameStruct);
userData.put("gender", "MALE");
userData.put("verified", Boolean.FALSE);
userData.put("userImage", "Rm9vYmFyIQ==");
mapper.writeValue(new File("user-modified.json"), userData);
你会发现在JSON转成Map.class的过程中没有指定Map的泛型类型,但是jackson也能正确的转换。jackson有一个自己的转换关系,能够对json数据进行默认转换。默认转换关系如下:
JSON TypeJava TypeobjectLinkedHashMap
Data Binding with Generics(泛型类型)
在上一个例子中,我们用了Map.class指定目标类型,jackson使用默认的类型进行转换。这里如果我们想指定具体的Key和Value类型,也是可以的。比如,想转换成Map
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() { });
是不是很牛逼!
此外,jackson的1.3版本又提供了一个TypeFactory指定Map
Tree Model Example
另外一种方式是使用树模型,树模型和XML的DOM方式类似。jackson会构建一课由JsonNode组成的树,里面的JsonNode暴露了一般需要用到的取值接口。当然,树里面的Node是JsonNode的子类,只有需要修改值的时候,你才有必要转换到子类型。
把json文本读取到一棵树:
ObjectMapper m = new ObjectMapper();
// can either use mapper.readTree(source), or mapper.readValue(source, JsonNode.class);
JsonNode rootNode = m.readTree(new File("user.json"));
// ensure that "last name" isn't "Xmler"; if is, change to "Jsoner"
JsonNode nameNode = rootNode.path("name");
String lastName = nameNode.path("last").getTextValue().
if ("xmler".equalsIgnoreCase(lastName)) {
((ObjectNode)nameNode).put("last", "Jsoner");
}
// and write it out:
m.writeValue(new File("user-modified.json"), rootNode);
直接在内存创建一棵树:
TreeMapper treeMapper = new TreeMapper();
ObjectNode userOb = treeMapper.objectNode();
Object nameOb = userRoot.putObject("name");
nameOb.put("first", "Joe");
nameOb.put("last", "Sixpack");
userOb.put("gender", User.Gender.MALE.toString());
userOb.put("verified", false);
byte[] imageData = getImageData(); // or wherever it comes from
userOb.put("userImage", imageData);
Streaming API Example
由于基本不会用到这种方式,所以,这里就不翻译了,接下来说点别的。
jackson怎么线程安全的使用ObjectMapper
ObjectMapper是线程安全的,所以我们可以直接定义一个全局的静态变量就可以了,不需要特别处理,如下:
class YourClass{
private static final ObjectMapper objectMapper = new ObjectMapper();
//do something with this objectMapper instance
}
ObjectMapper的常见属性设置
可能有时候我们需要对objectMapper做一些初始化设定,如下:
class YourClass{
private static final ObjectMapper objectMapper = new ObjectMapper();
static{
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)//指定属性字段转换成小写+下划线方式
.SETDATEFORMAT(new SimpleDateFormat(DATETIME_FORMAT))//指定日期时间转换成的样式(线程安全吗?)
.setSerializationInclusion(Include.NON_NULL);//指定不输出值为空的属性
}
//do something with this objectMapper instance
}
大家知道SimpleDateFormat是线程不安全的,那么这里直接设置了一个SimpleDateFormat,那么他安全吗?是安全的,内部有复制机制。
从gson更换到jackson让人不爽的一点
当POJO里面包含isXXX这样的字段的时候,gson转出来的结果是is_xxx,但是jackson转出来的是xxx,也就是自动抹掉了is。
解决办法有一个就是在对应属性上添加@JsonPropety(“is_xxx”)注解,指名属性名。就算如此,在一个些复杂的项目里面也会有其他的坑,所以尽量不要用isXXX来做属性名!!