为什么要替换 fastjson
工程里大量使用了 fastjson 作为序列化和反序列化框架, 甚至 ORM 在处理部分字段也依赖 fastjson 进行序列化和反序列化. 那么作为大量使用的基础框架, 为什么还要进行替换呢?
原因有以下几点:
fastjson 太过于侧重性能, 对于部分高级特性支持不够, 而且部分自定义特性完全偏离了 JSON 和 JS 规范导致和其他框架不兼容;
fastjson 文档缺失较多, 部分 Feature 甚至没有文档, 而且代码缺少注释较为晦涩;
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 的比较文章有很多, https://stackoverflow.com/ 上自行搜索, 下面仅推荐几篇 blog:
jackson vs gson https://www.baeldung.com/jackson-vs-gson JSON in Java https://www.baeldung.com/java-json the ultimate JSON library JSON-simple vs gson vs jackson vs JSON
在功能特性支持, 稳定性, 可扩展性, 易用性以及社区活跃度上 jackson 和 gson 差不多, 入门教程可以分别参考 baeldung jackson 系列 https://www.baeldung.com/category/json/jackson/ 以及 baeldung gson 系列 https://www.baeldung.com/tag/gson/ . 但是 jackson 有更多现成的类库兼容支持例如 jackson-datatype-commons-lang3, 以及更丰富的输出数据格式支持例如 jackson-dataformat-YAML, 而且 spring 框架默认使用 jackson, 因此最终我选择使用 jackson.
PS: Jackson 2.10.0 开始尝试基于新的 API 使用白名单机制来避免 RCE 漏洞, 详见, 效果尚待观察.
替换 fastjson
fastjson 常见的使用场景就是序列化和反序列化, 偶尔会有 JSONObject 和 JSONArray 实例的相关操作.
以下步骤的源码分析基于以下版本:
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 的静态方法(JSONObject 和 JSONArray 的静态方法也是来自于 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.000Z 或 2018-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 不支持该特性,但是可以通过 @JsonSetter 的 nulls() 和 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 的 < a href="https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization" ztid="249" ow="102" oh="39"> 默认是支持 Object.class、abstract classes、interfaces 属性的 AUTO Type,但是该特性容易导致安全漏洞,强烈建议使用 ObjectMapper.disableDefaultTyping() 设置为只允许 @JsonTypeInfo 生效 |
禁用属性智能匹配,例如下划线自动匹配驼峰等 | Feature.DisableFieldSmartMatch | 关闭 | - | - | jackson 可以通过 ObjectMapper.setPropertyNamingStrategy() 达到相同的目的,但这种是针对一个 json 串的统一策略,如果要在一个 json 串中使用不同的策略则可以使用 @JsonProperty.value() 指定字段名 |
启用 fastjson 的 autotype 功能,即根据 json 字符串中的 @type 自动选择反序列化的类 | Feature.SupportAutoType | 关闭 | ObjectMapper.DefaultTyping.* | 开启 | jackson 的 < a href="https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization" ztid="267" ow="102" oh="39"> 支持不同级别的 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 见
Serialization
fastjson 将 Java Bean 序列化成 JSON 字符串通常也是使用 com.alibaba.fastjson.JSON 的静态方法(JSONObject 和 JSONArray 的静态方法也是来自于 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 达到类似的效果,参见 < a href="https://www.baeldung.com/jackson-annotations" ztid="433" ow="78" oh="62">Jackson Annotation Examples https://www.baeldung.com/jackson-annotations |
序列化时消除对同一对象循环引用的问题 | 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 达到类似的效果,参见 < a href="https://www.baeldung.com/jackson-annotations" ztid="472" ow="78" oh="62">Jackson Annotation Examples https://www.baeldung.com/jackson-annotations |
不转义特殊字符, 已废弃,即使开启也无效 | 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 见
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 的用法可以参考中的 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 中 JSONObject 和 JSONArray 的源码:
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 出现大量修改, 因为缺少了 JSONObject 和 JSONArray 提供的多种便利的类型转换方法. 如果想要暂时保留 JSONObject 和 JSONArray, 此时可以采取一种取巧的方法.
暂时保留 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 对 fastjson 的数据类型的 binder.
欢迎大家使用或提 issues.
JSONPath
使用 https://github.com/json-path/JsonPath 就能轻松替换 fastjson 的 JSONPath, 而且功能比 fastjson 更强大.
只需参考 JsonProvider SPI 使用 JacksonJsonProvider 替代 https://github.com/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, 详见.
而在 jackson 中则是建议继承 SimpleBeanPropertyFilter.
参考文档
https://github.com/alibaba/fastjson https://github.com/FasterXML/jackson
Jackson 快速替换 Fastjson 之道
fastjson Features 说明
fastjson SerializerFeatures 说明
fastjson JSONField 说明
Jackson - Decide What Fields Get Serialized/Deserialized
来源: https://www.cnblogs.com/larva-zhh/p/11544317.html