赞
踩
jackson和gson的使用数量遥遥领先,但是jackson有更多现成的类库兼容支持例如jackson-datatype-commons-lang3
,以及更丰富的输出数据格式支持例如jackson-dataformat-yaml
,而且spring框架默认使用jackson,因此最终我选择使用jackson。(其实是公司要求)
声明一下,本文主要讲述改造思路,并没有粘贴源码(怕),但是保证看完之后能对fastjson和jackson的基本结构有更多认识,对fastjson改造有一定的思路。我们组的项目就是这么改的,历史两周,已开发自测完成。
想要改造好,必须先了解二者的基本结构,对两个框架的功能非常了解。接下来先介绍一下fastjson和jackson。
目前项目中大量的代码均使用了fastjson和json-lib两种框架(主要是fastjson),总计超过200个类中使用了这两种框架,且fastjson的代码api和jackson的相差较大,逐一改造十分困难,改造量巨大,想要改造的话需要先熟悉一下这两种框架,下面是二者的对比。
项目中主要使用的是fastjson的三个类或接口,分别是JSON
,JSONObject
,JSONObject
我们先来看一下fastjson的基本结构。
不难发现,JSON
作为JSONObject
和JSONArray
的父类,包含了绝大多数JSONObject
和JSONArray
的功能方法,包括最常用的toJSONString
,parseObject
,parseArray
,主要用来进行 json字符串 与 具体的业务对象 与 json对象(即框架定义的包装Json的对象,比如JSONObject和JSONArray)之间的转化。
而JSONObject
和JSONArray
主要的作用是用来存储数据。JSONObject
用来存储对象类型的json,并且实现了Map<String,Object>
接口,并有一个 Map<String,Object>
类型的属性来存储数据。JSONArray用来存储数组类型的json,实现了List<Object>
接口,有一个List<Object>
类型的属性来存储数据。
fastjson的结构还是非常简单的,总结一下,JSON
集成了所有功能型的方法。JSONObject
和 JSONArray
都实现了JSON
接口,分别用来存储对象和数组。
简单的结构应该是fastjson快速的原因之一,或许也是频繁发生安全漏洞的原因。
而jackson的结构就要复杂很多了。
jackson同样有包装json对象和json数组的类,分别为ObjectNode
和ArrayNode
,分别和fastjson的JSONObject
和JSONArray
对应。ObjectNode
和 ArrayNode
同样有一个共同的父类 “JsonNode
”,不过不同的是,这个JsonNode
并不像fastjson的JSON
一样,他并不包含功能类方法,只包含一些基本的操作数据的方法,比如isEmpty
、isObject
、asText
这种方法。并没有将 json字符串与具体对象与json对象之间转化的功能。
那么jackson框架如何进行转化操作的呢?他是通过一个叫做ObjectMapper
的类进行的格式转化的功能,而且方法名和fastjson相差确实很多。
如果只是这样的话,看起来差距也没有这么大,应该比较容易就能进行格式转化才对,但是最大的问题并不是这里。
最大的问题是fastjson和jackson的数据存储逻辑也有很大的不同。
首先就是jackson的ObjectNode
和ArrayNode
并没有实现Map
接口和List
接口,意味着在使用fastjson时直接用JSONObject
和JSONArray
作为Map
和List
进行参数传递是不行的。
其次,虽然两者都使用Map或者List来存储数据,但是两者Map或者List中可存储的数据类型不同,即Map和List的泛型参数不同,比如说,前面提到过,fastjson的JSONObject
使用 Map<String,Object>
进行存储对象的键值对,JSONArray
使用List<Object>
来存储数组元素。而jackson的ObjectNode
使用Map<String,JsonNode>
来存储数据,ArrayNode
使用List<JsonNode>
进行存储。这就意味着在fastjson中,对象键值对的value可以是任何类型,而ObjectNode
的value只能是JsonNode
,任何想要存进来的数据都需要转化成JsonNode
才行。数组也是同理。
由于jackson对于存储到ObjectNode
和ArrayNode
中数据的类型进行了类型的限制。所以,所有基本类型和String类型在jackson都有对应的 JsonNode
的实现类。比如String的实现类 TextNode
,boolean的实现类 BooleanNode
等,甚至连null值都有对应的实现类 NullNode
。
总结一下,jackson 对于所有基本类型和对象都有包装类,包括存储对象的ObjectNode
,存储数组的ArrayNode
,存储整形数字的IntNode
,存储String的TextNode
等。而且ObjectNode
中用来存储键值对的Map,其value只能是JsonNode
或其实现类。ArrayNode
中数组的元素也只能存储JsonNode
或其实现类。并且他们两个都没有实现Map接口。ObjectMapper是用来进行格式转化的工具类。下面是对应的一些类图。
介绍完两者的不同,接下来介绍一下到底如何改造,具体的改造思路如何。即如何重写JSON,JSONObject和JSONArray三个类。
由于项目中使用到JSONObject
,JSONArray
,JSON
等类的次数过多,并且fastjson和jackson的api差距过大,如果想要修改最少的代码,可以重写JSONObject
,JSONArray
,JSON
这三个类,并且 JSONObject
继承ObjectNode
类,JSONArray
继承ArrayNode
类,然后按照fastjson框架中对应的方法名实现对应的方法,直接全局替换包名就可以了。
首先需要重写JSON
类,这个类主要是作为一个工具类,后面重写的JSONObject
和JSONArray
都要在方法中调用JSON
工具类中的方法。那为什么不像fastjson那样用继承的方式呢?Java只支持单继承,JSONObject
和JSONArray
已经继承了ObjectNode
和ArrayNode
了。
因为fastjson和jackson默认的序列化规则可能,比如fastjson默认是支持json字符串的字段名使用单引号括起来,jackson不支持,fastjson默认支持json字符串末尾多一个逗号,jackson不支持等。而且这些不同都是可以通过添加配置来修改的,只需要将所需要的配置添加到ObjectMapper这个mapper中即可。所以需要先配置一个默认的ObjectMapper,使其和fastjson的默认序列化规则相同,即下面类中的 OBJECT_MAPPER
常量,并用createMapper配置了一些序列化和反序列化规则,至于最后的filter到后面再说。
配置好默认的mapper了,就可以开始重写fastjson中的方法了,JSON
的主要功能是进行数据之间的格式转化,即json字符串、具体的业务对象、json对象(JsonNode及其实现类)这三种数据之间的转化,其中json字符串和具体业务对象之间的转化比较简单,ObjectMappe
r中都有相应的方法。
而涉及到转化为JsonNode
等json对象时会比较复杂。总共包括两种情况,即将json字符串转化为json对象和将业务对象转化为json对象。
这里以将json字符串转化为json对象举例,用ObjectMapper
的默认方法最多将json字符串转化为ObjectNode
或者ArrayNode
等实现类,怎么将ObjectNode
或者ArrayNode
再转化为JSONObject
和JSONArray
呢?
其实,真实存储数据的是ObjectNode
中的map
和ArrayNode
中的List
,我们只需要将map
和list
中的数据转移过去就好。如果要将ObjectNode
转化为JSONObject
的话,只要new出一个JSONObject
来,将ObjectNode
中的map
取出并放到新创建的JSONObject
中即可,ArrayNode
也是同理。这里重写了一个JSONObject
的构造方法,接收的参数是ObjectNode
,方法中遍历ObjectNode
中map
的键值对然后插入到新的JSONObject
中。注意,这里新创建的JSONObject
中map
和原来ObjectNode
中的map
不是一个,并且map中的value也不是同一个对象,只是值相同而已,原因是 Map
的put
方法应该就是创建了一个新的对象然后再把值放进去(值拷贝)。
这里需要注意,如果json字符串中的json对象有多层,也就是对象中有对象或者对象中有数组,那么为了使每一层ArrayNode
和ObjectNode
都能被转化为JSONArray
和JSONObject
的话,需要递归进行上述操作。也就是这里的fromNode
方法。
public static JsonNode fromNode(JsonNode jsonNode){ if (jsonNode != null) { try { if(jsonNode.isObject()){ JSONObject jsonObject = new JSONObject((ObjectNode) jsonNode); Iterator<Map.Entry<String, JsonNode>> fields = jsonObject.fields(); while(fields.hasNext()){ Map.Entry<String, JsonNode> next = fields.next(); String key = next.getKey(); JsonNode value = next.getValue(); value = fromNode(value); jsonObject.put(key,value); } return jsonObject; }else if(jsonNode.isArray()){ JSONArray jsonArray = new JSONArray((ArrayNode) jsonNode); for(int i=0;i<jsonArray.size();i++){ JsonNode value = jsonArray.get(i); value = fromNode(value); jsonArray.set(i,value); } return jsonArray; }else{ return jsonNode; } } catch (Exception e) { logger.error("对象转化为JsonNode失败"); throw new MdmRuntimeException("对象转化为JsonNode失败", e); } } return null; }
经过上述操作,已经完成了fastjson中JSON的大部分功能,即进行数据格式的转化,剩下就是对JSONObject
和JSONArray
进行重写了。
JSONObject
和JSONArray
的大部分的方法就是对内部的数据进行获取或者添加,大部分是比较容易编写的,需要注意的包括几点:
自定义的JSONObject
的value只能是JsonNode
,而fastjson的JSONObject
可以的value可以是任何对象。这点在上文提到过,这就要求在往里塞数据时需要多转化一步,包括null值。JSONArray
也是同理。
JSONObject
的getJSONObject
方法使用了强转的方式,将拿到的ObjectNode
直接强转成JSONObject
后返回,因为前面的反序列化方法等已经将内部所有层的ObjectNode
全都转化为JSONObject
了,而所有的JSONObject
都是通过反序列化得到的,所以可以直接强转。
这里需要注意的是,不能通过get到ObjectNode
后通过上面讲述的fromNode
方法进行转化,上文提到过,转化后的JSONObject
和之前的ObjectNode
中的map不是一个,里面的value也不是同一个对象。
这样就完成了所有的基本功能,即数据之间的转化和对象的基本操作。其余就剩一些特殊情况的处理,比如特殊的序列化规则和反序列化的规则(比如字段过滤等)、对JSONObject
和JSONArray
的遍历(fastjson的实现了Map或者List,能使用Map和List的遍历方式,jackson的不行),将JSONObject
当作map进行参数传递等等,这些特殊情况都需要单独处理了。
修改完成之后两条常见的问题。
如果将JSONObject
或者JSONArray
直接作为controller接受的参数,或者接受的对象中有JSONObject
和JSONArray
作为属性,需要注意,会产生类型转化错误,会报错”不能将ObjectNode转化为JSONObject“,这时需要编写一个JSONObject
的反序列化器,使用前面将ObjectNode
对JSONObject
转化的方法进行转化,然后将反序列器通过注释(@JsonDeserialize(using = JSONObjectDeserializer.class)
加到JSONObject
类和JSONObject
属性上。JSONArray
同理。
如果想从JSONObject
或者JSONArray
中获取字符串类型的元素的话,注意,get拿到的元素为TextNode
类型,此时不能调用toString方法,需要调用asText方法,如果使用toString的话,会导致拿到的字符串多一对双引号。
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特性说明 | 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将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特性说明 | fastjson枚举 | fastjson默认状态 | jackson枚举 | jackson默认状态 | jackson特性说明 |
---|---|---|---|---|---|
输出key时是否使用双引号 | 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 | 关闭 | 建议关闭,开启会影响性能 |
Jackson的 convertValue
方法主常用于在不同类型的Java对象之间进行转换。这个方法属于 ObjectMapper
类,是Jackson库提供的一个非常强大的功能,可以让你轻松地在各种复杂的数据类型之间进行转换,包括从Java对象到Map、从Map到Java对象、甚至是从一种Java对象转换为另一种Java对象。
convertValue
方法的工作原理基于Jackson的序列化和反序列化能力。当你调用这个方法时,Jackson实际上执行了两个步骤:
convertValue
方法非常适合在以下场景中使用:
虽然 convertValue
方法非常强大和灵活,但在使用时也需要注意以下几点:
convertValue
方法在编译时不会检查类型安全,因此在运行时可能会遇到类型不匹配的问题。使用时应确保目标类型与源对象是兼容的。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。