一般來講,序列化json,實際上有2總方式 java
這倆種方式各有優劣。第一種方式毫無疑問,不須要開發者作什麼工做,直接調用序列化接口,輸出就是json。可是,若是須要特殊需求,好比須要將日期格式化按照yyyy-mm-dd 輸出,這些JSON工具能夠指定日期格式化輸出,好比FastJSON裏: git
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);
當前流行工具老是疲於應付這種「變態」序列化需求,不停的升級版本增長處理類來解決這些需求。有沒有終極解決辦法嗎? web
在回答這個問題前,讓咱們迴歸到我說的第二種序列化解決方法,就是手工編寫代碼。經過硬編碼貌似是是終極奧義。各類變態序列化需求都能經過硬編碼來解決。然而,這種終極奧義的問題是序列化太麻煩了。面對企業應用,互聯網應用中成百上千的模型,手工來序列化是不現實的,那麼,有沒有第三個辦法,既能方便序列化對象,有具備很強的序列化能力,應付各類變態需求呢? 正則表達式
以我看來,的確有第三個辦法,正如正則表達式那樣,能分析各類複雜文本,依靠的是(位置&指令)*。序列化JSON,實際上也須要相似正則表達式的思路,我抽象爲(locatoin:action)*. location 多是指一個對象的屬性名,也可能泛指某個類型的對象。action 是指序列化操做,好比忽略此location,或者一個排序動做,一個格式化動做,或者,是一個調用此對象的某個方法的動做等等。對於如下User對象,咱們能夠指定一系列的(Location:Action)* json
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,好比 app
~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百萬次,性能以下:
初步性能測試仍是不錯的。beetl-json略快一些,只須要1.238秒,不過考慮到beetl-json如今功能還未全,且對日期輸出作了優化,而FastJson沒有作。因此這三個之間,實際沒有太大的性能差距。
在當今web應用,互聯網應用火爆的年代,Json序列化是這些應用須要的一種基礎技術能力,基於(Location:Action)* 的JSON工具,相比於傳統(Annotation&SerializerFeature )* 的工具會更加靈活和功能強大,能高度知足程序員的序列化需求。beetl-json會進一步實踐這種思想。讓天下沒有難以序列化的對象