概述
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來做屬性名!!