(翻译)jackson五分钟教程

概述

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来做属性名!!

发布了141 篇原创文章 · 获赞 140 · 访问量 68万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章