alibaba fastjson的使用總結和心得

 

 

 
最初接觸alibaba fastjson是由於其性能上的優勢,對比原來採用codehause.jackson的解析,在hadoop平臺上的手動轉換對象有着將近1/3的性能提升,但隨着開發應用越來越多,漸漸地也發現了在其他方面的強大之處,例如直接轉化泛型(jackson對泛型的支持相對來說就比較差)。
 
Fastjson會自動處理對象中的泛型,將其解析成原來定義好的對象,建議一定要將泛型類型定義精確,以避免出現歧義,例如如果使用Map<String, Object> 就並不是一個定義良好的泛型,我們也並不知道Object對象真正表示的是什麼,而fastjson解析Object時,由於不知道其具體類型,只能將其轉化成JSONObject(結構類似一個Map,有着key和value)。
 

泛型類型解析

 
如果頂層類型中存在泛型,就會出現不知道確切類型的狀況,因爲Class是無法攜帶泛型信息的,Java中不存在一個類型爲List<String>.class,如果這樣使用,Fastjson會將對象解析成JSONObject。
 
alibaba fastjson提供了一套TypeReference相關的方法來解決這個問題,從源代碼來看定義不是很長:
 
public class TypeReference<T> {

    private final Type type;

    protected TypeReference(){
        Type superClass = getClass().getGenericSuperclass();

        type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() {
        return type;
    }

    public final static Type LIST_STRING = new TypeReference<List<String>>() {}.getType();
}
 
 
構造函數中做了兩件事,先從當前的Class.getGenericSuperclass,返回該Class表示的實體或直接超類的Type,重點在與,如果是參數化類型,返回的Type中會帶上該類型,我們看一下這個方法的定義及其作用:
 

getGenericSuperclass

public Type getGenericSuperclass()

返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的直接超類的 Type

如果超類是參數化類型,則返回的 Type 對象必須準確反映源代碼中所使用的實際類型參數。如果以前未曾創建表示超類的參數化類型,則創建這個類型。有關參數化類型創建過程的語義,請參閱 ParameterizedType 聲明。如果此 Class 表示 Object 類、接口、基本類型或 void,則返回 null。如果此對象表示一個數組類,則返回表示 Object 類的 Class 對象。

 

返回: 此對象所表示的類的超類 拋出: GenericSignatureFormatError - 如果常規類簽名不符合 Java Virtual Machine Specification, 3rd edition 規定的格式 TypeNotPresentException - 如果常規超類引用不存在的類型聲明 MalformedParameterizedTypeException - 如果常規超類引用的參數化類型由於某種原因無法實例化 從以下版本開始: 1.5
 
從文檔中可以看出,如果其超類是參數化類型,返回的Type就爲sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl類型,其繼承自java.lang.reflect.ParameterizedType,用來表示參數化類型。
 
參數化類型中就可以從getActualTypeArguments中獲取,因爲TypeReference只有一個參數化類型定義,因此可以確定返回的數組肯定有一個元素(如果是Map就存在兩個元素),這樣type就爲其參數化類型了,我們通過這個定義將對象的泛型傳入至後續的json處理邏輯中。
 

getActualTypeArguments

Type[] getActualTypeArguments()

返回表示此類型實際類型參數的 Type 對象的數組。

注意,在某些情況下,返回的數組爲空。如果此類型表示嵌套在參數化類型中的非參數化類型,則會發生這種情況。

 

返回:
表示此類型的實際類型參數的 Type 對象的數組
拋出:
TypeNotPresentException - 如果任何實際類型參數引用不存在的類型聲明
MalformedParameterizedTypeException - 如果任何實際類型參數引用參數化類型,該類型出於某種原因無法被實例化
從以下版本開始:
1.5
 
所以使用該類的方法只有一種,就是繼承,因此其構造方法爲protected,只能允許子類來調用,在構造該對象時,就需要在聲明最後加上{}來表示繼承關係。
 
java.util.Map<java.lang.String, com.xxx.commons.json.test.JsonTest$InnerObj>
 
 
 
 

關於類型轉換

 
如果是數組,由於某種對象的數組是存在對應的Class類型信息的,也就是說可以直接直接以Class的方式轉換成功。
 
當時如果沒有指定類型,就會轉換成JSONObject,在其中以key/value的方式(類似Map)存儲。
 
如果是數組,轉成JSONArray,在其中包裝JSONObject
 



 
 
 
如果我們使用TypeReference,指定其爲List<String>,而List中其實是對象,那麼直接轉換成String,此時字符串就爲json字符串。
 
如果事先不知道該json串的類型信息(json事先也無法知道其類型信息,而Java的序列化,是帶類型信息的,因此攜帶的內容也要比json串豐富地多,耗費網絡帶寬也大),或者在Map中寫不出統一的泛型,那麼就會發生隱性的類型轉換過程。在codehaus jackson中會將無法識別的json對象直接轉換成Map(key,value)格式,如果是fastjson,則轉換成JSONObject(其實也類似於json)。如果需要將該對象仍然轉換成json格式,不會有任何問題,但如果想要以原來的對象方式操作它,就會出現問題了(因爲沒有對象信息,除非手動將其轉換,但每部分都需要手寫代碼,比較麻煩)。
 
 
Map中字段爲null不被持久化的問題
 
如果Map中的某個屬性爲null,並不會被持久化,可以參考
 
 
如果想讓Map中的字段能夠正常輸出(這也是我們需要的),就需要在轉換toJSONString時設置特殊的Feature來完成該功能:
 
JSON.toJSONString(object, SerializerFeature.WriteMapNullValue)
 
 

關於Feature

 
 
Fastjson中存在多種SerializerFeature,可以按照需要使用,例如
 
  • SerializerFeature.UseSingleQuotes,//支持單引號
  • SerializerFeature.WriteDateUseDateFormat,//日期格式化 
  • SerializerFeature.WriteMapNullValue, // 輸出空置字段
  • SerializerFeature.WriteNullListAsEmpty, // list字段如果爲null,輸出爲[],而不是null
  • SerializerFeature.WriteNullNumberAsZero, // 數值字段如果爲null,輸出爲0,而不是null
  • SerializerFeature.WriteNullBooleanAsFalse, // Boolean字段如果爲null,輸出爲false,而不是null
  • SerializerFeature.WriteNullStringAsEmpty // 字符類型字段如果爲null,輸出爲"",而不是null
 
很多場景中,我們需要序列化的對象中存在循環引用,在許多的json庫中,這會導致stackoverflow。在功能強大的fastjson中,你不需要擔心這個問題。
 
使用@JSONField Annotation來定製化序列化字段的名稱,還可以使用@JSONCreator來指定構造函數來創建對象,配合@JSONField來確定字段的名稱。
 
如果使用新特性SerializerFeature.WriteClassName,可以在序列化的文本中加入@type信息,這樣就可以進行合理的反序列化,因爲知道了類型信息,可以通過默認的構造以及屬性名稱自動設置,此時反序列化的結果類似下面:
 
{"@type":"java.awt.Color","r":255,"g":0,"b":0,"alpha":255}
 
 
此時就可以進行自動識別,但此時輸出的文本就會多出一倍的空間,因此如果有着明確的類型,不建議使用這種方式。
 
String text = ...; // {"@type":"java.awt.Color","r":255,"g":0,"b":0,"alpha":255}
Color color = (Color) JSON.parse(text);
 
 
這樣可以解決一些序列化泛型中接口的問題,
 
還舉例說明了可以使用組合類型,fastjson對這種結構的反序列化有專門支持:
 
String text = ...; // [{/* header */}, {/* body */}]
Type[] types = new  Type[] {Header.class, Body.class};
List<Object> list = JSON.parseArray(text, types);
Header header = (Header) list.get(0);
Body body = (Body) list.get(1);
 
 
在使用WriteClassName這個Feature來進行寫Map等操作時,有一個問題,其中的List以及數組在被轉化回來時無法識別,會被自動轉換成JSONArray而不是原始的List,數組對象。如果用戶原來使用的List,這個問題可以得到完美的解決,看一下JSONArray的定義,
 
/**
 * @author wenshao[[email protected]]
 */
public class JSONArray extends JSON implements List<Object>, Cloneable, RandomAccess, Serializable { 
 
可以對其進行強制轉換List即可。
 
但是如果原始類型爲數組類型,則只能夠通過toArray方法,至於網友提出的能否直接使用Object[],其開發者給出了預期(只不過在1.2.9上也並未見其實現)。
 


 
 
但在1.2.9版本並沒有看到這個Feature,所以還是拭目以待吧。
 
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->
此外,我們在使用SpringMVC時,默認使用的json格式RequestMapper轉換實現應該是jackson實現的,當然可以將其替換成fastjson的實現:
 
<bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
    <!-- HandlerAdapter -->
    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean
                    class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                    <property name="features">
                        <array value-type="com.alibaba.fastjson.serializer.SerializerFeature">
                            <value>WriteDateUseDateFormat</value>
                            <value>WriteMapNullValue</value>
                        </array>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
  
 
 
 
 
  • 2d324cc7-638f-3509-8b35-f4d9178e5c90-thumb.png
  • 大小: 73.7 KB
  • 3c59120d-a2ad-365b-a8d0-cb02a74e7824-thumb.png
  • 大小: 21.7 KB
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章