Jackson替換fastjson

  • 2019 年 10 月 25 日
  • 筆記

為什麼要替換fastjson

工程里大量使用了fastjson作為序列化和反序列化框架,甚至ORM在處理部分欄位也依賴fastjson進行序列化和反序列化。那麼作為大量使用的基礎框架,為什麼還要進行替換呢?

原因有以下幾點:

  1. fastjson太過於側重性能,對於部分高級特性支援不夠,而且部分自定義特性完全偏離了json和js規範導致和其他框架不兼容;
  2. fastjson文檔缺失較多,部分Feature甚至沒有文檔,而且程式碼缺少注釋較為晦澀;
  3. fastjson的CVE bug監測較弱,很多CVE資料庫網站上有關fastjson的CVE寥寥無幾,例如近期的AutoType導致的高危漏洞,雖然和Jackson的PolymorphicDeserialization是同樣的bug,但是CVE網站上幾乎沒有fastjson的bug報告。

框架選型

參考mvnrepository json libraries,根據流行度排序後前十名框架:

  • jackson2(com.fasterxml.jackson)
  • gson
  • org.json
  • jackson1(com.codehuas.jackson)
  • fastjson
  • cheshire
  • json-simple

jackson1是已經過時的框架,因此可以忽略,cheshire和json-simple排名尚且不如fastjson,也忽略,剩餘jackson2、gson以及org.json,其中org.json的使用量(usage)遠小於jackson2(方便起見,下文均以jackson均指代jackson2)和gson,因此org.json也可以排除了。

關於jackson和gson的比較文章有很多,stackoverflow上自行搜索,下面僅推薦幾篇blog:

在功能特性支援、穩定性、可擴展性、易用性以及社區活躍度上 jackson 和 gson 差不多,入門教程可以分別參考baeldung jackson系列 以及 baeldung gson系列。但是jackson有更多現成的類庫兼容支援例如jackson-datatype-commons-lang3,以及更豐富的輸出數據格式支援例如jackson-dataformat-yaml,而且spring框架默認使用jackson,因此最終我選擇使用jackson。

PS: Jackson 2.10.0開始嘗試基於新的API使用白名單機制來避免RCE漏洞,詳見https://github.com/FasterXML/jackson-databind/issues/2195,效果尚待觀察。

替換fastjson

fastjson常見的使用場景就是序列化和反序列化,偶爾會有JSONObjectJSONArray實例的相關操作。

以下步驟的源碼分析基於以下版本:

  • fastjson v1.2.60
  • jackson-core v2.9.9
  • jackson-annotations v2.9.0
  • jackson-databind v2.9.9.3

Deserialization

fastjson將json字元串反序列化成Java Bean通常使用com.alibaba.fastjson.JSON的靜態方法(JSONObjectJSONArray的靜態方法也是來自於JSON),常用的有以下幾個API:

public static JSONObject parseObject(String text);    public static JSONObject parseObject(String text, Feature... features);    public static <T> T parseObject(String text, Class<T> clazz);    public static <T> T parseObject(String text, Class<T> clazz, Feature... features);    public static <T> T parseObject(String text, TypeReference<T> type, Feature... features);    public static JSONArray parseArray(String text);    public static <T> List<T> parseArray(String text, Class<T> clazz);

從方法入參就能猜到,fastjson在執行反序列化時的Parse行為由com.alibaba.fastjson.parser.Feature指定。研究parseObject的源碼後,發現底層最終都是使用的以下方法:

public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor, int featureValues, Feature... features) {          if (input == null) {              return null;          }            // featureValues作為基準解析特性開關值          // 入參features和featureValues取並集得到最終的解析特性          if (features != null) {              for (Feature feature : features) {                  featureValues |= feature.mask;              }          }            DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);            if (processor != null) {              if (processor instanceof ExtraTypeProvider) {                  parser.getExtraTypeProviders().add((ExtraTypeProvider) processor);              }                if (processor instanceof ExtraProcessor) {                  parser.getExtraProcessors().add((ExtraProcessor) processor);              }                if (processor instanceof FieldTypeResolver) {                  parser.setFieldTypeResolver((FieldTypeResolver) processor);              }          }            T value = (T) parser.parseObject(clazz, null);            parser.handleResovleTask(value);            parser.close();            return (T) value;      }

通過IDE搜索usage後,發現當沒有作為基準解析特性開關的featureValues入參時,都是使用的DEFAULT_PARSE_FEATURE作為基準解析特性開關,以下是JSON.DEFAULT_PARSE_FEATURE的實例化程式碼:

static {          int features = 0;          features |= Feature.AutoCloseSource.getMask();          features |= Feature.InternFieldNames.getMask();          features |= Feature.UseBigDecimal.getMask();          features |= Feature.AllowUnQuotedFieldNames.getMask();          features |= Feature.AllowSingleQuotes.getMask();          features |= Feature.AllowArbitraryCommas.getMask();          features |= Feature.SortFeidFastMatch.getMask();          features |= Feature.IgnoreNotMatch.getMask();          DEFAULT_PARSER_FEATURE = features;  }

fastjson還會從環境變數中讀取配置來修改DEFAULT_PARSER_FEATURE(雖然很少會有人這麼做),但最好還是通過實際運行一下程式來確認你的環境中的實際解析特性開關。

    @Test      public void printFastJsonDefaultParserFeature() {          for (Feature feature : Feature.values()) {              if (Feature.isEnabled(JSON.DEFAULT_PARSER_FEATURE, feature)) {                  System.out.println(feature);              }          }      }

fastjson 和 jackson的反序列化特性對照表

fastjson特性說明 fastjson枚舉 fastjson默認狀態 jackson枚舉 jackson默認狀態 jackson特性說明
Parser close時自動關閉為創建Parser實例而創建的底層InputStream以及Reader等輸入流 Feature.AutoCloseSource 開啟 JsonParser.Feature.AUTO_CLOSE_SOURCE 開啟 保持開啟
允許json字元串中帶注釋 Feature.AllowComment 關閉 JsonParser.Feature.ALLOW_COMMENTS 關閉 根據系統的json數據情況開啟
允許json欄位名不被引號包括起來 Feature.AllowUnQuotedFieldNames 開啟 JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES 關閉 根據系統的json數據情況開啟
允許json欄位名使用單引號包括起來 Feature.AllowSingleQuotes 開啟 JsonParser.Feature.ALLOW_SINGLE_QUOTES 關閉 根據系統的json數據情況開啟
將json欄位名作為字面量快取起來,即fieldName.intern() Feature.InternFieldNames 開啟 jackson默認使用InternCache快取了PropertyName
識別ISO8601格式的日期字元串,例如:2018-05-31T19:13:42.000Z2018-05-31T19:13:42.000+07:00 Feature.AllowISO8601DateFormat 關閉 jackson默認支援ISO8601格式日期字元串的解析,並且也可以通過ObjectMapper.setDateFormat指定解析格式
忽略json中包含的連續的多個逗號,非標準特性 Feature.AllowArbitraryCommas 關閉 jackson不支援該特性,且該特性是非標準特性,因此可以忽略
將json中的浮點數解析成BigDecimal對象,禁用後會解析成Double對象 Feature.UseBigDecimal 開啟 DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS 關閉 建議開啟
解析時忽略未知的欄位繼續完成解析 Feature.IgnoreNotMatch 開啟 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 開啟 jackson默認開啟遇到未知屬性需要拋異常,因此如要和fastjson保持一致則需要關閉該特性
如果你用fastjson序列化的文本,輸出的結果是按照fieldName排序輸出的,parser時也能利用這個順序進行優化讀取。這種情況下,parser能夠獲得非常好的性能 Feature.SortFeidFastMatch 關閉 fastjson內部處理邏輯,jackson不支援該特性,不影響功能
禁用ASM Feature.DisableASM 關閉 fastjson內部處理邏輯,jackson不支援該特性,不影響功能
禁用循環引用檢測 Feature.DisableCircularReferenceDetect 關閉 fastjson內部處理邏輯,jackson不支援該特性,不影響功能
對於沒有值的字元串屬性設置為空串 Feature.InitStringFieldAsEmpty 關閉 jackson不支援該特性,但是可以通過@JsonSetternulls()contentNulls()分別設置Bean以及Array/Collection的元素對null的處理方式。例如Nulls.AS_EMPTY就會將null設置為JsonDeserializer.getEmptyValue
非標準特性,允許將數組按照欄位順序解析成Java Bean,例如"[1001,"xx",33]"可以等價為"{"id": 10001, "name": "xx", "age": 33}" Feature.SupportArrayToBean 關閉 非標準特性,且使用場景較少,jackson不支援該特性
解析後屬性保持原來的順序 Feature.OrderedField 關閉
禁用特殊字元檢查 Feature.DisableSpecialKeyDetect 關閉
使用對象數組而不是集合 Feature.UseObjectArray 關閉 DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY 關閉 保持關閉
支援解析沒有setter方法的非public屬性 Feature.SupportNonPublicField 關閉 jaskson可以通過ObjectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)來達到相同的目的
禁用fastjson的AUTOTYPE特性,即不按照json字元串中的@type自動選擇反序列化類 Feature.IgnoreAutoType 關閉 jackson的PolymorphicDeserialization默認是支援Object.classabstract classesinterfaces屬性的AUTO Type,但是該特性容易導致安全漏洞,強烈建議使用ObjectMapper.disableDefaultTyping()設置為只允許@JsonTypeInfo生效
禁用屬性智慧匹配,例如下劃線自動匹配駝峰等 Feature.DisableFieldSmartMatch 關閉 jackson可以通過ObjectMapper.setPropertyNamingStrategy()達到相同的目的,但這種是針對一個json串的統一策略,如果要在一個json串中使用不同的策略則可以使用@JsonProperty.value()指定欄位名
啟用fastjson的autotype功能,即根據json字元串中的@type自動選擇反序列化的類 Feature.SupportAutoType 關閉 ObjectMapper.DefaultTyping.* 開啟 jackson的PolymorphicDeserialization支援不同級別的AUTO TYPE,但是這個功能容易導致安全漏洞,強烈建議使用ObjectMapper.disableDefaultTyping()設置為只允許@JsonTypeInfo生效
解析時將未用引號包含的json欄位名作為String類型存儲,否則只能用原始類型獲取key的值。例如String text="{123:"abc"}"在啟用了NonStringKeyAsString後可以通過JSON.parseObject(text).getString("123")的方式獲取到"abc",而在不啟用NonStringKeyAsString時,JSON.parseObject(text).getString("123")只能得到null,必須通過JSON.parseObject(text).get(123)的方式才能獲取到"abc" Feature.NonStringKeyAsString 關閉 非標準特性,jackson並不支援
自定義"{"key":value}"解析成Map實例,否則解析為JSONObject Feature.CustomMapDeserializer 關閉 jackson沒有相應的全局特性,但是可以通過TypeReference達到相同的效果
枚舉未匹配到時拋出異常,否則解析為null Feature.ErrorOnEnumNotMatch 關閉 DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL 關閉 fastjson默認解析為null,jackson則相反,默認會拋異常,建議採用jackson默認行為

反序列化fastjson和jackson的特性TestCase見DeserializationUseJacksonReplaceFastJsonTest.java

Serialization

fastjson將Java Bean序列化成json字元串通常也是使用com.alibaba.fastjson.JSON的靜態方法(JSONObjectJSONArray的靜態方法也是來自於JSON),常用的有以下幾個API:

public static String toJSONString(Object object);    public static String toJSONString(Object object, SerializerFeature... features);    public static String toJSONStringWithDateFormat(Object object, String dateFormat, SerializerFeature... features);    public static String toJSONString(Object object, boolean prettyFormat);    public static void writeJSONString(Writer writer, Object object, SerializerFeature... features);

從方法入參也能看出,在序列化時,fastjson的特性由SerializerFeature控制,研究toJSONString的源碼後,發現最終都會調用以下方法:

 public static String toJSONString(Object object, SerializeConfig config, SerializeFilter[] filters, String dateFormat, int defaultFeatures, SerializerFeature... features) {           SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);             try {               JSONSerializer serializer = new JSONSerializer(out, config);                 if (dateFormat != null && dateFormat.length() != 0) {                   serializer.setDateFormat(dateFormat);                   serializer.config(SerializerFeature.WriteDateUseDateFormat, true);               }                 if (filters != null) {                   for (SerializeFilter filter : filters) {                       serializer.addFilter(filter);                   }               }                 serializer.write(object);                 return out.toString();           } finally {               out.close();           }       }

通過IDE搜索usage後,發現當沒有作為基準解析特性開關的defaultFeatures入參時,都是使用的DEFAULT_GENERATE_FEATURE作為基準解析特性開關,以下是JSON.DEFAULT_GENERATE_FEATURE的實例化程式碼:

static {          int features = 0;          features |= SerializerFeature.QuoteFieldNames.getMask();          features |= SerializerFeature.SkipTransientField.getMask();          features |= SerializerFeature.WriteEnumUsingName.getMask();          features |= SerializerFeature.SortField.getMask();            DEFAULT_GENERATE_FEATURE = features;            config(IOUtils.DEFAULT_PROPERTIES);      }

fastjson還會從環境變數中讀取配置來修改DEFAULT_GENERATE_FEATURE(雖然很少會有人這麼做),但最好還是通過實際運行一下程式來確認你的環境中的實際解析特性開關。

    @Test      public void printFastJsonDefaultGenerateFeature() {          for (SerializerFeature feature : SerializerFeature.values()) {              if (SerializerFeature.isEnabled(JSON.DEFAULT_GENERATE_FEATURE, feature)) {                  System.out.println(feature);              }          }      }

fastjson 和 jackson的序列化特性對照表

fastjson特性說明 fastjson枚舉 fastjson默認狀態 jackson枚舉 jackson默認狀態 jackson特性說明
輸出的json欄位名被引號包含 SerializerFeature.QuoteFieldNames 開啟 JsonGenerator.Feature.QUOTE_FIELD_NAMES 開啟 保持開啟
序列化時使用單引號,而不是使用雙引號 SerializerFeature.UseSingleQuotes 關閉 jackson不支援該特性
序列化時,value為null的key或field也輸出 SerializerFeature.WriteMapNullValue 關閉 JsonInclude.Include.ALWAYS 開啟 建議按需選擇。注意SerializationFeature.WRITE_NULL_MAP_VALUES從2.9已廢棄,且會被JsonInclude.Include給覆蓋
序列化枚舉時使用枚舉類型的toString()方法,和SerializerFeature.WriteEnumUsingName互斥 SerializerFeature.WriteEnumUsingToString 關閉 SerializationFeature.WRITE_ENUMS_USING_TO_STRING 關閉 建議關閉,或者和反序列化的DeserializationFeature.READ_ENUMS_USING_TO_STRING保持一致
序列化枚舉時使用枚舉類型的name()方法,和SerializerFeature.WriteEnumUsingToString互斥 SerializerFeature.WriteEnumUsingName 開啟 jackson的默認行為,無需配置
序列化時對Date、Calendar等類型使用ISO8601格式進行格式化,否則以timestamp形式輸出Long數字 SerializerFeature.UseISO8601DateFormat 關閉 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 開啟 jackson和fastjson的默認行為都是將Date數據輸出為Long,建議根據不同的場景選擇是否需要格式化日期
序列化List類型數據時將null輸出為"[]" SerializerFeature.WriteNullListAsEmpty 關閉 可以通過PropertyFilter/SerializerFactory.withSerializerModifier(BeanSerializerModifier)任一一種方式達到相同效果,推薦使用PropertyFilter
序列化String類型的field時將null輸出為"" SerializerFeature.WriteNullStringAsEmpty 關閉 可以通過PropertyFilter/SerializerFactory.withSerializerModifier(BeanSerializerModifier)任一一種方式達到相同效果,推薦使用PropertyFilter
序列化Number類型的field時將null輸出為0 SerializerFeature.WriteNullNumberAsZero 關閉 可以通過PropertyFilter/SerializerFactory.withSerializerModifier(BeanSerializerModifier)任一一種方式達到相同效果,推薦使用PropertyFilter
序列化Boolean類型的field時將null輸出為false SerializerFeature.WriteNullBooleanAsFalse 關閉 可以通過PropertyFilter/SerializerFactory.withSerializerModifier(BeanSerializerModifier)任一一種方式達到相同效果,推薦使用PropertyFilter
序列化時忽略transient修飾的field SerializerFeature.SkipTransientField 開啟 MapperFeature.PROPAGATE_TRANSIENT_MARKER 關閉 建議保持關閉,通過@JsonIgnore或者FilterProvider來指定忽略的屬性
序列化時,如果未指定order,則將field按照getter方法的字典順序排序 SerializerFeature.SortField 開啟 MapperFeature.SORT_PROPERTIES_ALPHABETICALLY 關閉 建議關閉,排序會影響序列化性能(fastjson在反序列化時支援按照field順序讀取解析,因此排序後的json串有利於提高fastjson的解析性能,但jackson並沒有該特性)
t做轉義輸出,已廢棄,即使開啟也無效 SerializerFeature.WriteTabAsSpecial 關閉
格式化json輸出 SerializerFeature.PrettyFormat 關閉 SerializationFeature.INDENT_OUTPUT 關閉 建議保持關閉,格式化可以交給前端完成
序列化時把類型名稱寫入json SerializerFeature.WriteClassName 關閉 jackson可以通過@JsonTypeInfo達到類似的效果,參見Jackson Annotation Examples
序列化時消除對同一對象循環引用的問題 SerializerFeature.DisableCircularReferenceDetect 關閉 SerializationFeature.FAIL_ON_SELF_REFERENCES 開啟 保持開啟,避免循環引用
對斜杠’/’進行轉義 SerializerFeature.WriteSlashAsSpecial 關閉 jackson可以通過自定義Serializer實現相同效果,按需設置
將中文都會序列化為uXXXX格式,位元組數會多一些,但是能兼容IE 6 SerializerFeature.BrowserCompatible 關閉 jackson可以通過自定義Serializer實現相同效果,按需設置
全局修改日期格式,默認使用JSON.DEFFAULT_DATE_FORMAT SerializerFeature.WriteDateUseDateFormat 關閉 jackson可以通過@JsonFormat.pattern()ObjectMapper.setDateFormat()等方式實現相同效果
序列化時不把最外層的類型名稱寫入json SerializerFeature.NotWriteRootClassName 關閉 jackson可以通過@JsonRootName達到類似的效果,參見Jackson Annotation Examples
不轉義特殊字元,已廢棄,即使開啟也無效 SerializerFeature.DisableCheckSpecialChar 關閉
將Bean序列化時將field值按順序當成json數組輸出,而不是json object,同時不會輸出fieldName,例如:{"id":123,"name":"xxx"}會輸出成[123,"xxx"] SerializerFeature.BeanToArray 關閉 非標準特性,jackson並不支援
序列化Map時將非String類型的key作為String類型輸出,例如:{123:231}會輸出成{"123":231} SerializerFeature.WriteNonStringKeyAsString 關閉 非標準特性,jackson並不支援
序列化Byte、Short、Integer、Long、Float、Double、Boolean及其對應原始類型field時,如果屬性值為各自類型的默認值(如0、0F、0L),則不會輸出該屬性 SerializerFeature.NotWriteDefaultValue 關閉 非標準特性,jackson並不支援
序列化時將()><以unicode編碼輸出 SerializerFeature.BrowserSecure 關閉 jackson可以通過自定義Serializer實現相同效果,按需設置,通常可以交給前端處理
序列化時忽略沒有實際屬性對應的getter方法 SerializerFeature.IgnoreNonFieldGetter 關閉
序列化時把非String類型數據當作String類型輸出 SerializerFeature.WriteNonStringValueAsString 關閉 jackson有一個類似的特性JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS可以將數字作為字元串輸出,但沒有覆蓋所有非String類型
序列化時忽略會拋異常的getter方法 SerializerFeature.IgnoreErrorGetter 關閉
序列化時將BigDecimal使用toPlainString()輸出 SerializerFeature.WriteBigDecimalAsPlain 關閉 JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN 關閉 按需開啟
序列化時對Map按照Key進行排序 SerializerFeature.MapSortField 關閉 SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS 關閉 建議關閉,開啟會影響性能

序列化fastjson和jackson的特性TestCase見SerializationUseJacksonReplaceFastJsonTest.java

Annotation

fastjsonzhu相對於jackson來說註解的功能劃分的並沒有那麼細,因此fastjson的一個註解可能等價於jackson多個註解的組合。

@JSONPOJOBuilder

指定反序列化時創建java對象使用的build方法,對應jackson的@JsonPOJOBuilder

@JSONCreator

指定反序列化時創建java對象使用的構造方法,對應jackson的@JsonCreator

@JSONField

指定序列化和反序列化field時的行為。反序列化時,等價於@JsonProperty + @JsonDeserialize + @JsonUnwrapped + @JsonFormat+ @JsonAlias
序列化時,等價於@JsonProperty + @JsonSerialize + @JsonUnwrapped + @JsonFormat + @JsonRawValue + @JsonView

@Retention(RetentionPolicy.RUNTIME)  @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })  public @interface JSONField {      // 序列化和反序列化時的欄位順序,等價於jackson的@JsonProperty.index()      int ordinal() default 0;        // 序列化和反序列化時的欄位名稱映射,等價於jackson的@JsonProperty.value()      String name() default "";        // 序列化和反序列化時的數據格式(日期格式、16進位等等),等價於jackson的@JsonFormat.shape() + @JsonFormat.pattern()      String format() default "";        // 欄位是否序列化,等價於jackson的@JsonProperty.access()      boolean serialize() default true;        // 欄位是否反序列化,等價於jackson的@JsonProperty.access()      boolean deserialize() default true;        // 序列化特性,等價於jackson的@JsonProperty.with()      SerializerFeature[] serialzeFeatures() default {};        // 反序列化特性,等價於jackson的@JsonFormat.with()      Feature[] parseFeatures() default {};        // 對屬性進行打標,便於在序列化時進行exclude或include,等價於jackson的@JsonView      String label() default "";        // 序列化時將欄位內容直接輸出,不經過轉義,等價於jackson的@JsonRawValue      boolean jsonDirect() default false;        // 指定序列化時使用的Serializer Class,等價於jackson的@JsonSerialize      Class<?> serializeUsing() default Void.class;        // 指定反序列化時使用的Deserializer Class,等價於jackson的@JsonDeserialize      Class<?> deserializeUsing() default Void.class;        // 指定反序列化時使用的欄位別名,等價於jackson的@JsonAlias      String[] alternateNames() default {};        // 將欄位的子屬性映射到父節點上,等價於jackson的@JsonUnwrapped      boolean unwrapped() default false;        // 指定序列化時欄位為null時使用的默認值,等價於jackson的@JsonProperty.defaultValue()      String defaultValue() default "";  }

unwrapped的用法可以參考AnnotationUseJacksonReplaceFastJsonTest.java中的testJSONFieldUnwrapped

@JSONType

指定序列化和反序列化一個Java Bean時的行為。

@Retention(RetentionPolicy.RUNTIME)  @Target({ ElementType.TYPE })  public @interface JSONType {        // 是否使用asm優化,jackson無對應特性      boolean asm() default true;        // 序列化和反序列化時的field排序,等價於jackson的@JsonPropertyOrder.value()      String[] orders() default {};        // 序列化和反序列化時包含的field,等價於jackson的      String[] includes() default {};        // 序列化和反序列化時忽略的field,等價於jackson的@JsonIgnoreProperties      String[] ignores() default {};        // 序列化特性,等價於jackson的@JsonProperty.with()      SerializerFeature[] serialzeFeatures() default {};        // 反序列化特性,等價於jackson的@JsonFormat.with()      Feature[] parseFeatures() default {};        // 序列化時是否依據field字母順序排序,等價於jackson的@JsonPropertyOrder.alphabetic()      boolean alphabetic() default true;        // 反序列化多態類型時,如果根據其他typeName等方式無法找到正確的子類時,默認使用的子類,等價於jackson的@JsonTypeInfo.defaultImpl()      Class<?> mappingTo() default Void.class;        // 反序列化時指定java bean builder類(必須是@JSONPOJOBuilder註解的類),等價於jackson的@JsonDeserialize.builder()      Class<?> builder() default Void.class;        // 聲明這個類型的別名,反序列化多態類型時使用,等價於jackson的@JsonTypeName      String typeName() default "";        // 反序列化某個介面或抽象類或父類的子類時指定根據哪個欄位的值和子類的typeName相等來決定具體實現類,等價於jackson的@JsonTypeInfo.use() = Id.CUSTOM + @JsonTypeInfo.property()      String typeKey() default "";        // 反序列化某個介面或抽象類或父類的子類時指定可以反序列化的子類類型,等價於jackson的@JsonSubTypes      Class<?>[] seeAlso() default{};        // 指定序列化時使用的Serializer Class,等價於jackson的@JsonSerialize      Class<?> serializer() default Void.class;        // 指定反序列化時使用的Deserializer Class,等價於jackson的@JsonDeserialize      Class<?> deserializer() default Void.class;        // 序列化時,如果filed是枚舉類型,則和普通的java bean一樣輸出枚舉的filed,而不是通常使用的Enum.name()值,jackson沒有對應特性      boolean serializeEnumAsJavaBean() default false;        // 指定json和Java bean之間的欄位名稱映射策略,等價於jackson的@JsonNaming      PropertyNamingStrategy naming() default PropertyNamingStrategy.CamelCase;        // 指定序列化時使用的Serialize filter,等價於jackson的@JsonFilter      Class<? extends SerializeFilter>[] serialzeFilters() default {};  }

JSONObject & JSONArray

首先來看看fastjon中JSONObjectJSONArray的源碼:

public class JSONObject extends JSON implements Map<String, Object>, Cloneable, Serializable, InvocationHandler {        private final Map<String, Object> map;      ...  }
public class JSONArray extends JSON implements List<Object>, Cloneable, RandomAccess, Serializable {        private static final long  serialVersionUID = 1L;      private final List<Object> list;      protected transient Object relatedArray;      protected transient Type   componentType;      ...  }

從源碼就可以發現,JSONObject實際是一個Map<String, Object>,而JSONArray實際是一個List<JSONObject>。因此可以將JSONObject類型改為Map<String, Object>,而JSONArray類型改為List<Object>
但是這種方式就會導致上層API出現大量修改,因為缺少了JSONObjectJSONArray提供的多種便利的類型轉換方法。如果想要暫時保留JSONObjectJSONArray,此時可以採取一種取巧的方法。

暫時保留JSONObject & JSONArray的過渡方法

jackson官方提供了對org.json庫的數據類型支援jackson-datatype-json-org,因此可以將com.alibaba.fastjson.JSONObject替換為org.json.JSONObject
com.alibaba.fastjson.JSONArray替換為org.json.JSONArray,這兩個類庫的對象API大致相同,當然一些細小的改動還是避免不了的。
如果想完全不改上層程式碼,那也可以參考jackson-datatype-json-org
jackson-datatype-json-lib自己實現jackson對fastjson的數據類型的binder。

larva-zhang/jackson-datatype-fastjson歡迎大家使用或提issues。

JSONPath

使用json-path/JsonPath就能輕鬆替換fastjson的JSONPath,而且功能比fastjson更強大。
只需參考JsonProvider SPI使用JacksonJsonProvider替代json-path/JsonPath默認的JsonSmartJsonProvider即可。

自定義擴展

自定義Deserializer

fastjson中實現自定義Deserializer的方法通常是實現ObjectDeserializer介面的deserialze方法

<T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName);

在jackson中實現自定義Serializer的方法則通常是繼承StdDeserializer抽象類,重寫deserialize方法

public abstract T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException;

自定義Serializer

fastjson中實現自定義Serializer的方法通常是實現ObjectSerializer介面的write方法

void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException;

在jackson中實現自定義Serializer的方法則通常是繼承StdSerializer抽象類,重寫serialize方法

public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException;

自定義Serialize Filter

fastjson中提供了6種SerializeFilter,詳見fastjson/wiki/SerializeFilter
而在jackson中則是建議繼承SimpleBeanPropertyFilter

參考文檔