Location:Action,新的JSON序列化的思路

pjp 9年前發布 | 17K 次閱讀 JSON JSON開發包

序列化Json,是當前火爆的互聯網世界的一種最基礎的技術之一,最常用的是采用 Annotation&SerializerFeature 方式,國內流行的fastjson,還有國外流行的jackson都是這樣。然而,這種方法有一定的局限性。會遇到各種奇葩序列化要求而很難做到。或者 json工具作者不得不按照奇葩需求開發更多的序列化實現。本文介紹了常規思路的瓶頸,以及新思路,附帶一個beetl-json實現(用了2周)來說明 這個新的思路

通常來說,序列化json,實際上有2總方式

  • 通過當前流行的JSON工具。
  • 編寫代碼,手工序列化
  • </ul>

    這倆種方式各有優劣。第一種方式毫無疑問,不需要開發者做什么工作,直接調用序列化接口,輸出就是json。但是,如果需要特殊需求,比如需要將日期格式化按照yyyy-mm-dd 輸出,這些JSON工具可以指定日期格式化輸出,比如FastJSON里:

    SerializeConfig mapping = new SerializeConfig();
    String dateFormat = "yyyy-MM-dd";  
    mapping.put(Date.class, new SimpleDateFormatSerializer(dateFormat));
    String json = JSON.toJSONString(obj,mapping);

     在Jackon里,代碼也是類似,如:

    ObjectMapper objectMapper = new ObjectMapper();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    objectMapper.setDateFormat(sdf);
    objectMapper.writeValue(writer, obj);


    對于這種常見需求,JSON工具總有通過Annotation&&SerializerFeature組合來解決 ,但也有應對不了的,如一下一些問題
    1. 對象里有個字段是Calendar,我想格式化yyyy-MM-dd 輸出。
    2. 對象里另外有個字段也是Calendar,我想僅僅輸出time(Long類型)
    3. 對象及其關聯對象的id我都不想輸出。
    4. 如果某個對象的集合屬性為null,則僅僅輸出[], 但另外一個集合屬性未null,則輸出null。

    當前流行工具總是疲于應付這種“變態”序列化需求,不停的升級版本增加處理類來解決這些需求。有沒有終極解決辦法嗎?

        在回答這個問題前,讓我們回歸到我說的第二種序列化解決方法,就是手工編寫代碼。通過硬編碼貌似是是終極奧義。各種變態序列化需求都能通過硬編碼來解決。然而,這種終極奧義的問題是序列化太麻煩了。面對企業應用,互聯網應用中成百上千的模型,手工來序列化是不現實的,那么,有沒有第三個辦法,既能方便序列化對象,有具有很強的序列化能力,應付各種變態需求呢?

        以我看來,的確有第三個辦法,正如正則表達式那樣,能分析各種復雜文本,依靠的是(位置&指令)*。序列化JSON,實際上也需要類似正則表達式的思路,我抽象為(locatoin:action)*. location 可能是指一個對象的屬性名,也可能泛指某個類型的對象。action 是指序列化操作,比如忽略此location,或者一個排序動作,一個格式化動作,或者,是一個調用此對象的某個方法的動作等等。對于以下User對象,我們可以指定一系列的(Location:Action)*

    public class User{  
        String name="joel";
        int age =12;    
        double salary=12.32266;
        Date bir = new Date();
        Customer  customer = new Customer();
        List<Customer>  list = new ArrayList<Customer>();
        List<Customer>  deleteList = null;
        //getter and setter     方法必須有,在此忽略
    }


    Location:Action  描述
    name:i
    name 指的是User對象的name屬性,i代表一個操作,意思ignore。表示此字段不需要序列化
    ~d:f/yyyy-MM-dd/
    ~d 表示所有日期類型(包含其子類),動作是調用一個格式化函數f,參數是yyyy-MM-dd
    bir:$.getTime
    bir是User對象bir屬性,輸出時調用其getTime方法輸出毫秒時間
    ~*::O/name, age/
    將User類的name,age放到前面優先顯示
    ~*:Ci/name,id/
    如果User實例被引用過(循環引用),則僅僅輸出id,和name。即避免了循環引用問題,也同時有明確的輸出
    deleteList :?null->[]
    deleteList是User的屬性,如果為null,則輸出[]

    如上幾個簡單例子可以看到基于(Location:Action)序列化功能的強大和靈活,甚至可以 組合Action,比如

    ~L/java.util.Calendar*/:$.getTime->f/yyyy-MM-dd/,可以解釋為對于所有對象類型為java.util.Calendar及其子類,輸出的時候,先調用$.getTime,獲得Date,然后再格式化輸出。

    由此可見,如果定義好Location,以及提供一定數量的Action,和內置一些表達式操作(如?empty->dosomething),便具有超過傳統的基于(Annotation&&SerializerFeature)的JSON工具的序列化能力。不僅僅如此,通過指定好policy,policy=(location:action)*,可以實現對同一對象的不同序列化策略。比如代碼:

    String json = JsonTool.serialize(User,"id:i"); //不輸出id
    //or  指定一個序列化策略,age,name先輸出,適合有特殊需求的對象或者無法注解(第三方)對象
    String json2 = JsonTool.serialize(User,"~*:O/age,name/"));


    由此可見,序列化JSON的第三種道路,即"(Location:Action)*" 非常接近我認為的序列化JSON終極奧義,他具有當前流行"(Annotation&SerializerFeature)*" 操作簡便性,也具有硬編碼序列化的的靈活性。我寫了一個beetl-json 作為驗證,斷斷續續花了2周時間和犧牲了周末:),自我感覺效果不錯的,我貼一些Beetl-JSON 代碼可以看看

    JsonTool.addLocationAction("~d","f/yyyy.MM.dd/");   
    JsonTool.addLocationAction("~L/java.util.Calendar*/","$.getTime->f/yyyy-MM-dd/");
    //類json格式的策略,用逗號分開多個locationAction
    JsonTool.addPolicy("~f:f/#.##/,~c:?null->[]");
    // 默認是緊湊輸出,使用true,將換行和縮進  
    JsonTool.pretty = true;
    //序列化User
    String json = JsonTool.serialize(User);
    //or  指定一個序列化策略,age,name先輸出,適合有特殊需求的對象或者無法注解(第三方)對象
    String json2 = JsonTool.serialize(User,"~*:O/age,name/"));


    User對象定義如下:

    @Json(
        policys={
                @JsonPolicy(location="name", action="nn/newUserName/"),
                @JsonPolicy(location="deleteList", action="?empty->[]")                     
        }
    )
    public class User{  
        String name="joel";
        int age =12;    
        double salary=12.32266;
        Customer  customer = new Customer();
        List<Customer>  list = new ArrayList<Customer>();
        List<Customer>  deleteList = null;
        //getter and setter     方法必須有,在此忽略
    }


        序列化性能現在還未完全考慮中,因為只做了2周,功能還不全,還屬于驗證階段,此時如果比較性能,比較占便宜,我把我初步的性能測試代碼放到 performance test 里了,包含了FastJSON,Jackson.在我老式筆記本里,單線程序列化一個普通對象1百萬次,性能如下:

    Location:Action,新的JSON序列化的思路

    初步性能測試還是不錯的。beetl-json略快一些,只需要1.238秒,不過考慮到beetl-json現在功能還未全,且對日期輸出做了優化,而FastJson沒有做。所以這三個之間,實際沒有太大的性能差距。

      在當今web應用,互聯網應用火爆的年代,Json序列化是這些應用需要的一種基礎技術能力,基于(Location:Action)* 的JSON工具,相比于傳統(Annotation&SerializerFeature )* 的工具會更加靈活和功能強大,能高度滿足程序員的序列化需求。beetl-json會進一步實踐這種思想。讓天下沒有難以序列化的對象。

    來自:http://my.oschina.net/u/567839/blog/413072

 本文由用戶 pjp 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!