fastjson 過濾不需要序列化的屬性
JavaJSON技術框架選型與例項
JSON
JSON英文全稱為JavaScriptObject Natation,採用key:value鍵值對的方式存貯資料,與xml格式相比,JSON是一種輕量級的資料交換格式;不要被Javascript這個單詞迷惑,實際上JSON只是一種資料格式,與具體語言並無關係。JSON已被廣泛應用於業界,比如目前NoSQL資料庫存貯大都採用key:value存貯結構,以Mongo為例,其指令碼語法甚至直接使用Javascript;在資料傳輸時,採用JSON格式也被廣泛應用,大部分開放API都開放JSON模式的資料輸出;在ajax請求資料時,json格式也被廣泛推薦。json更多資訊的可以檢視json官方網站http://json.org。
Java transient關鍵字
JAVA規範原文The transient marker is not fully specified by the Java LanguageSpecification but is used in object serialization to mark member variables thatshould not be serialized.為了方便存貯和網路傳輸,java有系列化物件機制,transient可用來指定當前不想被系列化的成員物件。舉個例子說明transient的應用,在Mongo+Morphia開源專案下,如果對Java PO的成員指定transient,那麼該成員資料將不會被存入Mongo資料庫。另外一種應用場景就是這裡要講到的JSON,如果JAVA PO使用了Refrence(Mongo的Refrence)或者LazyLoading(可以理解成Hibernate LazyLoading概念),那麼大部分的開源JAVA JSON相關專案,會自動載入這些Refrence、LazyLoading物件,如果PO形成相互引用,那就會形成死迴圈,即使沒有形成死迴圈,大量不必要的資料被輸出到客戶端對資源的浪費也不容小覷。加上transient是一種解決辦法。
基於JAVA的JSON主要開源專案及其對比
Json開源專案非常多,如org.json、 JSON-Lib、jsontool、Jackson、Gson、SimpleJSON等等,後來專門查看了幾種json開源測試資料對比後,決定採用fastjson。展示兩組測試資料。首先來看大俠wangym(原部落格http://wangym.iteye.com/blog/738933)對Jackson、JSON-Lib、Gson的測試結果
JSON轉Bean,5個執行緒併發,約200位元組物件,1千萬次轉換:
Jackson |
JSON-lib |
Gson |
|
吞吐量 |
64113.7 |
8067.4 |
13952.8 |
總耗時(秒) |
155 |
1238 |
700 |
Bean轉JSON,5個執行緒併發,約200位元組物件,1千萬次轉換:
Jackson |
JSON-lib |
Gson |
|
吞吐量 |
54802 |
15093.2 |
17308.2 |
總耗時(秒) |
181 |
661 |
560 |
顯而易見,無論是哪種形式的轉換,Jackson > Gson > Json-lib。
Jackson的處理能力甚至高出Json-lib有10倍左右
然後再拿溫少的fastjson與JSON-Lib、Simple-JSON、Jackson效能測試對比資料
效能對比
測試案例 |
JSON-Lib |
Simple-JSON |
Fastjson |
Jackson |
IntArray1000Decode |
3,626 |
1,431 |
563 |
596 |
StringArray1000Decode |
2,698 |
2,283 |
677 |
774 |
Map100StringDecode |
515 |
597 |
208 |
230 |
功能對比
特性 |
JSON-Lib |
Simple-JSON |
Fastjson |
Jackson |
序列化支援陣列 |
不支援 |
不支援 |
支援 |
支援 |
序列化支援Enum |
不支援 |
不支援 |
支援 |
支援 |
支援JavaBean |
不直接支援 |
不直接支援 |
支援 |
支援 |
可以看到Fastjson在效能方面,超越目前的所有java json proccesor,包括jackson。
FastJson應用例項
1、利用Jquery ajax請求fastjson資料來顯示使用者列表例子實現
//定義一個User PO物件
public class User implements Serializable{
private static final long serialVersionUID = 1738399846398814044L;
private String userid;
private String username;
//注意這裡使用了Refrence及Lazyloading相關的引用
@Refrence
private UserDetail userDeatil;
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this. username = username;
}
public UserDetail getUserDetail() {
return userDetail;
}
public void setUserDetail (UserDetail userDetail) {
this. userDetail = userDetail;
}
}
//定義一個UserDetail PO物件
public class UserDetail implements Serializable{
private static final long serialVersionUID = 1738399846398814045L;
private String address;
public String getAddress() {
return address;
}
public void setAddress (String address) {
this. address = address;
}
}
編寫Action,輸出List<User>,這裡使用偽碼
….
List<User> ls= userService.getUserList();
PrintWriterout = null;
try {
out = getResponse().getWriter();
out.write(JSON.toJSONString(ls));
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
…
編寫jquery ajax請求打出使用者列表
$.ajax({
type:"GET",
dataType:"json",
cache:false,
success: function(users){
var html=””;
if(users.length>0){
for(var i in users){
html=html+”username:”+users[i]+username+” address:”+users[i].userDetail.address;
}
alert(html);
}
});
2、如何解決Refrence及LazyLoading引起的死迴圈問題?
從上述例子可以看到fastjson會正確取出userDetail下的address資料,實際上所有的json開源專案都支援這種關聯取出。但有時候我們並不需要userDetail下的資料,如果自動載入一堆無關的資料,甚至產生死迴圈,怎麼解決呢?
第一種辦法:
前面已經講過,加上transient關鍵字,如給User PO的UserDetail定義改成
private transient UserDetailuserDeatil;
第二種辦法:
第一種辦法是通用的辦法,使用其他json開源專案,也可以達到效果,在FastJson下還可以使用@JSONField(serialize=false)
@JSONField(serialize=false)
private transient UserDetailuserDeatil;
當然JSONField還有其他引數可以指定,以實現成員定製序列化,一般情況下,如果我們確定成員可以為非序列化,首先建議使用transient。但有時候指定了transient會引起其他問題,假如User物件下有長欄位remark,如果給remark指定了transient,那麼在比如使用Mongo資料庫情況下,會導致頁面提交的remark資料不能被儲存到資料庫,其他沒有加transient關鍵字的欄位能正常儲存。這時就可以使用@JSONField來解決問題。
第三種辦法:
假如有更進一步的優化,比如場景A的時候需要系列化remark,而在場景B的時候又不需要系列化,那就使用fastjson定製過濾器,fastjson可以按name、property、value三種過濾,以property例,重寫獲取List<user>這段偽碼:
….
List<User> ls= userService.getUserList();
PropertyFilter filter = new PropertyFilter() {
publicboolean apply(Object source, String name, Object value) {
if("remark".equals(name)) {
return true;
}
returnfalse;
}
};
SerializeWriter sw = new SerializeWriter();
JSONSerializer serializer = new JSONSerializer(sw);
serializer.getPropertyFilters().add(filter);
serializer.write(ls);
PrintWriterout = null;
try {
out = getResponse().getWriter();
out.write(sw.toString());
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
…
這樣在碰到場景B時就使用第三種辦法把remark這個成員給過濾掉,在場景A的情況下不加過濾器即可。