用了几年的 Fastjson,我最终替换成了Jackson!
Posted xhmj12
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用了几年的 Fastjson,我最终替换成了Jackson!相关的知识,希望对你有一定的参考价值。
相关阅读:一个90后员工猝死的全过程
作者:larva-zhh
来源:www.cnblogs.com/larva-zhh/p/11544317.html
为什么要替换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
关于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常见的使用场景就是序列化和反序列化,偶尔会有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的PolymorphicDeserialization默认是支持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的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
的静态方法(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 达到类似的效果,参见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中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-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
。
更多精彩内容,关注我们▼▼
- EOF -
PS:如果觉得我的分享不错,欢迎大家随手点赞、在看
大家一起在评论区聊聊呗~
茉莉花,别名茉莉,拉丁文名:Jasminum sambac (L.) Ait,木犀科、素馨属直立或攀援灌木,高达3米。小枝圆柱形或稍压扁状,有时中空,疏被柔毛。叶对生,单叶,叶片纸质,圆形、椭圆形、卵状椭圆形或倒卵形,两端圆或钝,基部有时微心形,在上面稍凹入或凹起,下面凸起,细脉在两面常明显,微凸起,除下面脉腋间常具簇毛外,其余无毛;裂片长圆形至近圆形,先端圆或钝。果球形,呈紫黑色。花期5-8月,果期7-9月。茉莉的花极香,为著名的花茶原料及重要的香精原料;花、叶药用治目赤肿痛,并有止咳化痰之效。吴王子子驹亡走闽越,怨东瓯杀其父,常劝闽越击东瓯。至建元三年,闽越发兵围东瓯。东瓯食尽,困,太史公曰:余每读虞书,至於君臣相敕,维是几安,而股肱不良,万事堕坏,未尝不流涕也。成王作颂,推己惩艾,悲彼家难,可不谓战战恐惧,善守善终哉?君子不为约则修德,满则弃礼,佚能思初,安能惟始,沐浴膏泽而歌咏勤苦,非大德谁能如斯!传曰“治定功成,礼乐乃兴”。海内人道益深,其德益至,所乐者益异。满而不损则溢,盈而不持则倾。凡作乐者,所以节乐。君子以谦退为礼,以损减为乐,乐其如此也。以为州异国殊,情习不同,故博采风俗,协比声律,以补短移化,助流政教。天子躬於明堂临观,而万民咸荡涤邪秽,斟酌饱满,以饰厥性。故云雅颂之音理而民正,嘄噭之声兴而士奋,郑卫之曲动而心淫。及其调和谐合,鸟兽尽感,而况怀五常,含好恶,自然之势也? 治道亏缺而郑音兴起,封君世辟,名显邻州,争以相高。自仲尼不能与齐优遂容於鲁,虽退正乐以诱世,作五章以剌时,犹莫之化。陵迟以至六国,流沔沈佚,遂往不返,卒於丧身灭宗,并国於秦。 秦二世尤以为娱。丞相李斯进谏曰:“放弃诗书,极意声色,祖伊所以惧也;轻积细过,恣心长夜,纣所以亡也。”赵高曰:“五帝、三王乐各殊名,示不相袭。上自朝廷,下至人民,得以接欢喜,合殷勤,非此和说不通,解泽不流,亦各一世之化,度时之乐,何必华山之騄耳而后行远乎?”二世然之。 高祖过沛诗三侯之章,令小兒歌之。高祖崩,令沛得以四时歌鳷宗庙。孝惠、孝文、孝景无所增更,於乐府习常肄旧而已。 至今上即位,作十九章,令侍中李延年次序其声,拜为协律都尉。通一经之士不能独知其辞,皆集会五经家,相与共讲习读之,乃能通知其意,多尔雅之文。 汉家常以正月上辛祠太一甘泉,以昏时夜祠,到明而终。常有流星经於祠坛上。使僮男僮女七十人俱歌。春歌青阳,夏歌硃明,秋歌西昚,冬歌玄冥。世多有,故不论。 又尝得神马渥洼水中,复次以为太一之歌。曲曰:“太一贡兮天马下,霑赤汗兮沫流赭。骋容与兮跇万里,今安匹兮龙为友。”後伐大宛得千里马,马名蒲梢,次作以为歌。歌诗曰:“天马来兮从西极,经万里兮归有德。承灵威兮降外国,涉流沙兮四夷服。”中尉汲黯进曰:“凡王者作乐,上以承祖宗,下以化兆民。今陛下得马,诗以为歌,协於宗庙,先帝百姓岂能知其音邪?”上默然不说。丞相公孙弘曰:“黯诽谤圣制,当族。” 凡音之起,由人心生也。人心之动,物使之然也。感於物而动,故形於声;声相应,故生变;变成方,谓之音;比音而乐之,及干戚羽旄,谓之乐也。乐者,音之所由生也,其本在人心感於物也。是故其哀心感者,其声噍以杀;其乐心感者,其声啴以缓;其喜心感者,其声发以散;其怒心感者,其声粗以厉;其敬心感者,其声直以廉;其爱心感者,其声和以柔。六者非性也,感於物而后动,是故先王慎所以感之。故礼以导其志,乐以和其声,政以壹其行,刑以防其奸。礼乐刑政,其极一也,所以同民心而出治道也。 凡音者,生人心者也。情动於中,故形於声,声成文谓之音。是故治世之音安以乐,其正和;乱世之音怨以怒,其正乖;亡国之音哀以思,其民困。声音之道,与正通矣。宫为君,商为臣,角为民,徵为事,羽为物。五者不乱,则无怗懘之音矣。宫乱则荒,其君骄;商乱则搥,其臣坏;角乱则忧,其民怨;徵乱则哀,其事勤;羽乱则危,其财匮。五者皆乱,迭相陵,谓之慢。如此则国之灭亡无日矣。郑卫之音,乱世之音也,比於慢矣。桑间濮上之音,亡国之音也,其政散,其民流,诬上行私而不可止。 凡音者,生於人心者也;乐者,通於伦理者也。是故知声而不知音者,禽兽是也;知音而不知乐者,众庶是也。唯君子为能知乐。是故审声以知音,审音以知乐,审乐以知政,而治道备矣。是故不知声者不可与言音,不知音者不可与言乐知乐则几於礼矣。礼乐皆得,谓之有德。德者得也。是故乐之隆,非极音也;食飨之礼,非极味也。清庙之瑟,硃弦而疏越,一倡而三叹,有遗音者矣。大飨之礼,尚玄酒而俎腥鱼,大羹不和,有遗味者矣。是故先王之制礼乐也,非以极口腹耳目之欲也,将以教民平好恶而反人道之正也。 人生而静,天之性也;感於物而动,性之颂也。物至知知,然后好恶形焉。好恶无节於内,知诱於外,不能反己,天理灭矣。夫物之感人无穷,而人之好恶无节,则是物至而人化物也。人化物也者,灭天理而穷人欲者也。於是有悖逆诈伪之心,有淫佚作乱之事。是故彊者胁弱,众者暴寡,知者诈愚,勇者苦怯,疾病不养,老幼孤寡不得其所,此大乱之道也。是故先王制礼乐,人为之节:衰麻哭泣,所以节丧纪也;钟鼓干戚,所以和安乐也;婚姻冠笄,所以别男女也;射乡食飨,所以正交接也。礼节民心,乐和民声,政以行之,刑以防之。礼乐刑政四达而不悖,则王道备矣。 乐者为同,礼者为异。同则相亲,异则相敬。乐胜则流,礼胜则离。合情饰貌者,礼乐之事也。礼义立,则贵贱等矣;乐文同,则上下和矣;好恶著,则贤不肖别矣;刑禁暴,爵举贤,则政均矣。仁以爱之,义以正之,如此则民治行矣。 乐由中出,礼自外作。乐由中出,故静;礼自外作,故文。大乐必易,大礼必简。乐至则无怨,礼至则不争。揖让而治天下者,礼乐之谓也。暴民不作,诸侯宾服,兵革不试,五刑不用,百姓无患,天子不怒,如此则乐达矣。合父子之亲,明长幼之序,以敬四海之内。天子如此,则礼行矣。 大乐与天地同和,大礼与天地同节。和,故百物不失;节,故祀天祭地。明则有礼乐,幽则有鬼神,如此则四海之内合敬同爱矣。礼者,殊事合敬者也;乐者,异文合爱者也。礼乐之情同,故明王以相沿万石君名奋,其父赵人也,姓石氏。赵亡,徙居温。高祖东击项籍,过河内,时奋年十五,为小吏,侍高祖。高祖与语,爱其恭敬,问曰:“若何有?”对曰:“奋独有母,不幸失明。家贫。有姊,能鼓琴。”高祖曰:“若能从我乎?”曰:“原尽力。”於是高祖召其姊为美人,以奋为中涓,受书谒,徙其家长安中戚里,以姊为美人故也。其官至孝文时,积功劳至大中大夫。无文学,恭谨无与比。 文帝时,东阳侯张相如为太子太傅,免。选可为傅者,皆推奋,奋为太子太傅。及孝景即位,以为九卿;迫近,惮之,徙奋为诸侯相。奋长子建,次子甲,次子乙,次子庆,皆以驯行孝谨,官皆至二千石。於是景帝曰:“石君及四子皆二千石,人臣尊宠乃集其门。”号奋为万石君。 孝景帝季年,万石君以上大夫禄归老于家,以岁时为朝臣。过宫门阙,万石君必下车趋,见路马必式焉。子孙为小吏,来归谒,万石君必朝服见之,不名。子孙有过失,不谯让,为便坐,对案不食。然后诸子相责,因长老肉袒固谢罪,改之,乃许。子孙胜冠者在侧,虽燕居必冠,申申如也。僮仆如也,唯谨。上时赐食於家,必稽首俯伏而食之,如在上前。其执丧,哀戚甚悼。子孙遵教,亦如之。万石君家以孝谨闻乎郡国,虽齐鲁诸儒质行,皆自以为不及也。 建元二年,郎中令王臧以文学获罪。皇太后以为儒者文多质少,今万石君家不言而躬行,乃以长子建为郎中令,少子庆为内史。 建老白首,万石君尚无恙。建为郎中令,每五日洗沐归谒亲,入子舍,窃问侍者,取亲中稖厕窬,身自浣涤,复与侍者,不敢令万石君知,以为常。建为郎中令,事有可言,屏人恣言,极切;至廷见,如不能言者。是以上乃亲尊礼之。 万石君徙居陵里。内史庆醉归,入外门不下车。万石君闻之,不食。庆恐,肉袒请罪,不许。举宗及兄建肉袒,万石君让曰:“内史贵人,入闾里,里中长老皆走匿,而内史坐车中自如,固当!”乃谢罢庆。庆及诸子弟入里门,趋至家。 万石君以元朔五年中卒。长子郎中令建哭泣哀思,扶杖乃能行。岁馀,建亦死。诸子孙咸孝,然建最甚,甚於万石君。 建为郎中令,书奏事,事下,建读之,曰:“误书!‘马’者与尾当五,今乃四,不足一。上谴死矣!”甚惶恐。其为谨慎,虽他皆如是。 万石君少子庆为太仆,御出,上问车中几马,庆以策数马毕,举手曰:“六马。”庆於诸子中最为简易矣,然犹如此。为齐相,举齐国皆慕其家行,不言而齐国大治,为立石相祠。 元狩元年,上立太子,选群臣可为傅者,庆自沛守为太子太傅,七岁迁为御史大夫。 元鼎五年秋,丞相有罪,罢。制诏御史:“万石君先帝尊之,子孙孝,其以御史大夫庆为丞相,封为牧丘侯。”是时汉方南诛两越,东击朝鲜,北逐匈奴,西伐大宛,中国多事。天子巡狩海内,修上古神祠,封禅,兴礼乐。公家用少,桑弘羊等致利,王温舒之属峻法,兒宽等推文学至九卿,更进用事,事不关决於丞相,丞相醇谨而已。在位九岁,无能有所匡言。尝欲请治上近臣所忠、九卿咸宣罪,不能服,反受其过,赎罪。 元封四年中,关东流民二百万口,无名数者四十万,公卿议欲请徙流民於边以適之。上以为丞相老谨,不能与其议,乃赐丞相告归,而案御史大夫以下议为请者。丞相惭不任职,乃上书曰:“庆幸得待罪丞相,罢驽无以辅治,城郭仓库空虚,民多流亡,罪当伏斧质,上不忍致法。原归丞相侯印,乞骸骨归,避贤者路。”天子曰:“仓廪既空,民贫流亡,而君欲请徙之,摇荡不安,动危之,而辞位,君欲安归难乎?”以书让庆,庆甚惭,遂复视事。 庆文深审谨,然无他大略,为百姓言。後三岁馀,太初二年中,丞相庆卒,谥为恬侯。庆中子德,庆爱用之,上以德为嗣,代侯。後为太常,坐法当死,赎免为庶人。庆方为丞相,诸子孙为吏更至二千石者十三人。及庆死後,稍以罪去,孝谨益衰矣。 建陵侯卫绾者,代大陵人也。绾以戏车为郎,事文帝,功次迁为中郎将,醇谨无他。孝景为太子时,召上左右饮,而绾称病不行。文帝且崩时,属孝景曰:“绾长者,善遇之。”及文帝崩,景帝立,岁馀不噍呵绾,绾日以谨力。 景帝幸上林,诏中郎将参乘,还而问曰:“君知所以得参乘乎?”绾曰:“臣从车士幸得以功次迁为中郎将,不自知也。”上问曰:“吾为太子时召君,君不肯来,何也?”对曰:“死罪,实病!”上赐之剑。绾曰:“先帝赐臣剑凡六,剑不敢奉诏。”上曰:“剑,人之所施易,独至今乎?”绾曰:“具在。”上使取六剑,剑尚盛,未尝服也。郎官有谴,常蒙其罪,不与他将争;有功,常让他将。上以为廉,忠实无他肠,乃拜绾为河间王太傅。吴楚反,诏绾为将,将河间兵击吴楚有功,拜为中尉。三岁,以军功,孝景前六年中封绾为建陵侯。 其明年,上废太子,诛栗卿之属。上以为绾长者,不忍,乃赐绾告归,而使郅都治捕栗氏。既已,上立胶东王为太子,召绾,拜为太子太傅。久之,迁为御史大夫。五岁,代桃侯舍为丞相,朝奏事如职所奏。然自初官以至丞相,终无可言。天子以为敦厚,可相少主,尊宠之,赏赐甚多。 为丞相三岁,景帝崩,武帝立。建元年中,丞相以景帝疾时诸官囚多坐不辜者,而君不任职,免之。其後绾卒,子信代。坐酎金失侯。 塞侯直不疑者,南阳人也。为郎,事文帝。其同舍有告归,误持同舍郎金去,已而金主觉,妄意不疑,不疑谢有之,买金偿。而告归者来而归金,而前郎亡金者大惭,以此称为长者。文帝称举,稍迁至太中大夫。朝廷见,人或毁曰:“不疑状貌甚美,然独无柰其善盗嫂何也!”不疑闻,曰:“我乃无兄。”然终不自明也。 吴楚反时,不疑以二千石将兵击之。景帝後元年,拜为御史大夫。天子修吴楚时功,乃封不疑为塞侯。武帝建元年中,谚曰“力田不如逢年,善仕不如遇合”,固无虚言。非独女以色媚,而士宦亦有之。 昔以色幸者多矣。至汉兴,高祖至暴抗也,然籍孺以佞幸;孝惠时有闳孺。此两人非有材能,徒以婉佞贵幸,与上卧起,公卿皆因关说。故孝惠时郎侍中皆冠鵕璘,贝带,傅脂粉,化闳、籍之属也。两人徙家安陵。 孝文时中宠臣,士人则邓通,宦者则赵同、北宫伯子。北宫伯子以爱人长者;而赵同以星气幸,常为文帝参乘;邓通无伎能。邓通,蜀郡南安人也,以濯船为黄头郎。孝文帝梦欲上天,不能,有一黄头郎从後推之上天,顾见其衣裻带後穿。觉而之渐台,以梦中阴目求推者郎,即见邓通,其衣後穿,梦中所见也。召问其名姓,姓邓氏,名通,文帝说焉,尊幸之日异。通亦愿谨,不好外交,虽赐洗沐,不欲出。於是文帝赏赐通巨万以十数,官至上大夫。文帝时时如邓通家游戏。然邓通无他能,不能有所荐士,独自谨其身以媚上而已。上使善相者相通,曰“当贫饿死”。文帝曰:“能富通者在我也。何谓贫乎?”於是赐邓通蜀严道铜山,得自铸钱,“邓氏钱”布天下。其富如此。 文帝尝病痈,邓通常为帝唶吮之。文帝不乐,从容问通曰:“天下谁最爱我者乎?”通曰:“宜莫如太子。”太子入问病,文帝使唶痈,唶痈而色难之。已而闻邓通常为帝唶吮之,心惭,由此怨通矣。及文帝崩,景帝立,邓通免,家居。居无何,人有告邓通盗出徼外铸钱。下吏验问,颇有之,遂竟案,尽没入邓通家,尚负责数巨万。长公主赐邓通,吏辄随没入之,一簪不得著身。於是长公主乃令假衣食。竟不得名一钱,寄死人家。 孝景帝时,中无宠臣,然独郎中令周文仁,仁宠最过庸,乃不甚笃。 今天子中宠臣,士人则韩王孙嫣,宦者则李延年。嫣者,弓高侯孽孙也。今上为胶东王时,嫣与上学书相爱。及上为太子,愈益亲嫣。嫣善骑射,善佞。上即位,欲事伐匈奴,而嫣先习胡兵,以故益尊贵,官至上大夫,赏赐拟於邓通。时嫣常与上卧起。江都王入朝,有诏得从入猎上林中。天子车驾跸道未行,而先使嫣乘副车,从数十百骑,骛驰视兽。江都王望见,以为天子,辟从者,伏谒道傍。嫣驱不见。既过,江都王怒,为皇太后泣曰:“请得归国入宿卫,比韩嫣。”太后由此嗛嫣。嫣侍上,出入永巷不禁,以奸闻皇太后。皇太后怒,使使赐嫣死。上为谢,终不能得,嫣遂死。而案道侯韩说,其弟也,亦佞以上是关于用了几年的 Fastjson,我最终替换成了Jackson!的主要内容,如果未能解决你的问题,请参考以下文章
用了几年的 Fastjson,我最终替换成了Jackson!
用了几年的 Fastjson,我最终替换成了Jackson!
用了几年的 Fastjson,我最终替换成了Jackson!