(翻譯)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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章