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",
url:"/user/getuserlist", //假設這是你配置後的action地址
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的情況下不加過濾器即可。
更多fastjson信息可以查看http://code.alibabatech.com/wiki/display/FastJSON/Home