fastjson, Jackson与Gson 各场景试验

本文罗列了笔者遇到的JSON使用中不得不注意的feature和坑。
就本次【粗糙的】
兼容性试验来看,Jackson > Gson > fastjson
易用性试验来看,Gson = fastjson > Jackson(Jackson的filter,真jr难用)
也可能在之后使用中出现打脸情况。也欢迎读者评论区打脸。
fastjson版本:1.2.58(特别标明,因为fastjson在1.2.29与1.2.58表现不同。1.2.51以下有安全漏洞)
gson版本:2.3.1
jackson-bind版本:2.9.3
场景1:JSON规范,互相parse
    就Map<Long, String>的序列化方法,fastjson和另两位想法不同。
    源类定义:

private Integer a;
private String b;
private List<Short> cList;
private Map<Long, String> dMap;
private Boolean e;
private Object f;

    目标类定义:

private Integer a;
private String b;
private List<Short> cList;
private Map<Long, String> dMap;
private Object f;

    序列化字符串:

gson:     {"a":1,"b":"bbbbb","cList":[3],"dMap":{"4":"ddd"},"f":6}
fastjson: {"a":1,"b":"bbbbb","cList":[3],"dMap":{4:"ddd"},"f":6}
jackson:  {"a":1,"b":"bbbbb","e":null,"f":6,"clist":[3],"dmap":{"4":"ddd"}}

    在dmap这个字段上,所有版本的fastjson默认配置下,特立独行,并没有给map的key带上引号。需要手动指定SerializerFeature.WriteNonStringKeyAsString。

// 个人认为这是fastjson的最佳实践
static {
    JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNonStringKeyAsString.getMask();
}

若不指定SerializerFeature.WriteNonStringKeyAsString,会导致Gson, Jackson无法识别fastjson序列化生成的json串,兼容性极差。
   
由于此问题,如果fastjson不指定SerializerFeature.WriteNonStringKeyAsString,在进行3x3的9次互相parse中,gson和jackson都表示不认识fastjson序列化的字符串,抛出了错误。

    此外,对null的字段e,只有jackson一板一眼地将null值写了出来。更改配置可以让Jackson忽略null值的序列化:

mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

场景2:fastjson的历史兼容问题
    给定String:{"dMap":{1:"1.0"}} (注意,这个string是fastjson生成的。它本身在jackson和gson眼里就是非法的。)
    fastjson在1.2.29和1.2.58中,对JSON.parseObject(obj, Map.class)的返回表现不同。
    1.2.29中,反序列化得到的dMap,key是String。这与jackson兼容。
    1.2.58中,反序列化得到的dMap,key是Integer。将此map通过Spring Controller返回时,提交给jackson序列化,会报错。
    这个问题,也可以通过场景1中的SerializerFeature.WriteNonStringKeyAsString绕过。因为有了这个选项,就不会生成出现此场景给定的迷之非法“json”字符串。当然,如果串是别人传入的,就只能自己注意此差异了。

场景3:对数字的认知差异
    f字段是一个原始类对象。对此对象赋值为1(int),在反序列化之后,jackson和fastjson都认定为Integer,但gson会认定为Double。
    没有声明类所以本来没什么优劣,但实际使用中,Double.toString会自动补一个'.0'在末尾,可能造成问题(见场景4)。

场景4:parse为map
    使用Map.class接反序列化结果时,jackson能保持字面原样,内层的dMap识别为Map<String, String>。
    fastjson内层的dMap识别成自定义的Map的继承类JSONObject。
    gson表现很迷,会将原Integer转换成Double,且作者表示JSON中数字是没有区别的,所以坚持在Gson中不做区别:https://github.com/google/gson/issues/1084。解决方案见 https://stackoverflow.com/questions/36508323/how-can-i-prevent-gson-from-converting-integers-to-doubles,关键字:MapDeserializerDoubleAsIntFix。

场景5:源对象比目标对象字段多
    由于源对象比目标对象多一个字段e,jackson的默认配置下会抛出UnrecognizedPropertyException。特例:如果多出的字段由于(为null且被Gson序列化)或(其他原因),序列化为string没有表现出来时,jackson会逃过一劫,正常反序列化。
    通过配置jackson的objectMapper,禁用反序列化选项FAIL_ON_UNKNOWN_PROPERTIES,可以解决此问题:

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

场景6:字符串中的小数,反序列化为整数

    当字符串中为小数,反序列化为整数时,不同框架表现不同。
    jackson:自动转为整数。
    Gson:抛出NumberFormatException。
    fastjson:不同版本表现不同。1.2.29自动转(BigDecimal.intValue),1.2.29.sec04如果有小数部分,抛出ArithmeticException,1.2.58对小数位数<100的自动转。

场景7:HttpServletRequest的序列化
    HttpServletRequest是Spring中声明Controller并对请求做若干操作时的常用类。但此类的对象,使用3种不同的序列化工具都无法正常序列化。
    fastjson抛出JSONException, Caused by: java.lang.IllegalStateException: s=DISPATCHED i=true a=NOT_ASYNC
    jackson与gson抛出stackOverflow(多半是由于对象中存在环形引用)。

场景8:序列化规则指定
    业务场景是:对字段中,以"set"打头和"Iterator"结尾的属性,不做序列化。
    fastjson: JSON.toJSONString中,指定SerializeFilter

private static PropertyFilter getPropertyFilter(String ignore) {
    return (source, name, value) -> !name.equals(ignore) && !name.startsWith("set") && value != null && !name.endsWith("Iterator");
}
private static PropertyFilter getPropertyFilter() {
    return getPropertyFilter(StringUtils.EMPTY);
}
public static String toJSONString(Object src) {
    return JSON.toJSONString(obj, getPropertyFilter());
}

    Gson: 生成Gson对象时,指定SerializeStrategy。见 https://www.baeldung.com/gson-exclude-fields-serialization
    Jackson: 必须使用注解在源类上!对源类没操作权限时,需要自定义MixIn类,并在Mapper里指定类名映射!然后自行实现SimpleBeanPropertyFilter!绕过手段:https://blog.csdn.net/ligeforrent/article/details/94757247

发布了66 篇原创文章 · 获赞 17 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/ligeforrent/article/details/93759524