主頁 > 後端開發 > 用了幾年的 Fastjson,我最終替換成了Jackson!

用了幾年的 Fastjson,我最終替換成了Jackson!

2021-06-21 06:07:39 後端開發

作者:larva-zhh
來源:www.cnblogs.com/larva-zhh/p/11544317.html

為什么要替換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 vs gson
  • JSON in Java
  • the ultimate json library json-simple vs gson vs jackson vs json

在功能特性支持、穩定性、可擴展性、易用性以及社區活躍度上 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 = https://www.cnblogs.com/javastack/archive/2021/06/20/(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.JSONObjectcom.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

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2021最新版)

2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!

3.阿里 Mock 工具正式開源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式發布,全新顛覆性版本!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/288188.html

標籤:其他

上一篇:python實戰技巧之兩個字典,如何實作鍵同則值相加【不等長或等長】

下一篇:我愛學英語系列叢筆記

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more