1. 程式人生 > >fastjson 過濾不需要序列化的屬性

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-lib10倍左右

然後再拿溫少的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) {

       thisaddress = 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的情況下不加過濾器即可。