FastJson问题记录

概述

作为一个Java开发者,FastJson可以说是必用的JSON序列化和反序列化工具。

FastJson是一个高性能JSON处理器,无外部依赖。FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要指定引用。FastJson采用独创算法,将parse速度提升到极致,超过所有json库。如果有性能要求,可使用Gson将bean转换json确保数据正确,使用FastJson将Json转换Bean。

Spring MVC/Boot默认使用Jackson来进行model & view的转化,pom.xml文件加入FastJson的dependency。

此文记录一下工作这么多年遇到的各种问题。

问题

JSONObject与Map互相转换

  1. Map转换成JSONObject
JSONObject jsonObj = JSONObject.parseObject(JSON.toJSONString(itemMap));
  1. JSONObject转换成Map
// 方法1
Map<String, Object> map1 = JSONObject.toJavaObject(jsonObj, Map.class);
// 方法2
Map<String, Object> map2 = JSON.parseObject(jsonObj, Map.class);

write javaBean error, fastjson version 1.2.73, class xxx, fieldName : 0

在这里插入图片描述

报错的代码片段(其中list是从数据表查询得到的list<po>数据):
return JSONObject.toJSONString(list);
报错的类xxx,即POJO定义如下(已剔除查询语句未返回的字段,除isactive外):

public class DashboardCategory {
    private String categoryName;
    private Boolean isactive;
    private String permission;

    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName == null ? null : categoryName.trim();
    }

    public Boolean getIsactive() {
        return isactive;
    }

    public void setIsactive(Boolean isactive) {
        this.isactive = isactive;
    }
}

显然,这个类里面并没有一个属性字段fieldName是0,报错根源是isactive,而这个字段的定义不符合规范。
解决方法:

  1. isactive重命名为isActive,改动面较大;
  2. POJO使用lombok @Data注解,并删除getter/setter这种毫无意义的代码;
  3. JSONObject.toJSONString(list, SerializerFeature.IgnoreErrorGetter);,使用忽视配置。

附:网络上千篇一律抄来抄去的解决方案@JSONField(serialize =false)根本就不能这样玩,因为使用到这个POJO实体类的接口非常多,前端可能需要展示或使用这个字段,根本就不能无脑地不序列化到responseBody中。

循环引用

经常遇到的一个问题,返回的responseBody带有类似

{
    "$ref": "$.data.editorList[0]"
},

这样的脏数据。解决方案:

JSONObject.toJSONString(dataobj, SerializerFeature.DisableCircularReferenceDetect);

ClassCastException

一次项目重构时遇到的问题,测试扔过来的报错日志:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.alibaba.fastjson.JSONObject
        at com.aaa.cbd.platform.service.facebook.GetAdReports$1.run(GetAdReports.java:1100)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

找到代码行数,设置断点,很快可以拿到一模一样的报错信息:
在这里插入图片描述
没道理的啊,重构前没有报错的;最后定位到重构前后使用的fastjson版本不对应,重构前版本为1.2.29,重构后版本为1.2.73。

针对这个ClassCastException,解决方法有两种:

  1. 降低fastjson版本(推荐)
  2. 将报错的地方调整为(不推荐,因为不知道有多少地方的代码有类似问题):
JSONObject adConentJson = JSONObject.parseObject(JSONObject.toJSONString(adConentObj)); 

field ignore

一个常见的开发实践,将接口请求参数和响应结果落库保存,方便问题排查和回溯。接口请求参数或响应结果很多,如果过滤掉不期望落库的数据字段呢?

import com.alibaba.fastjson.support.spring.PropertyPreFilters;

private String getQueryInputStr(JSONObject paramJson) {
	// 若干个想要过滤的字段
    String[] excludeProperties = {"appkey", "serialNumber"};
    PropertyPreFilters filters = new PropertyPreFilters();
    PropertyPreFilters.MySimplePropertyPreFilter excludeFilter = filters.addFilter();
    excludeFilter.addExcludes(excludeProperties);
    return JSONObject.toJSONString(paramJson, excludeFilter);
}

insert fastjson to db

数据表一般定义为下划线,MQ或三方接口responseBody也经常定义为下划线。拿到三方数据时,此时如果直接落库,则无需转化为驼峰命名,也就不需要在Mybatis文件里面定义繁琐的resultMap,即数据表字段和PO实体类属性映射关系(当然,一般我们可以使用mybatis-generator等工具快速生成该映射关系),然后再引用这个resultMap

<resultMap id="BaseResult" type="com.aaa.cbd.platform.po.ChannelTiktokApp">
	<result column="advertiser_id" property="advertiserId" jdbcType="VARCHAR"/>
</resultMap>

所以看看Mybatis能不能支持直接insert/update JSONObject数据?
mapper层接口:

void insertBatchChannelTiktokApp(List<JSONObject> list);

mapper.xml文件简化后片段:

<insert id="insertBatchChannelTiktokApp" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
    insert into channel_tiktok_app(partner)
    values
    <foreach collection="list" item="app" index="index" separator=",">
        (#{app.partner})
    </foreach>
</insert>

service层代码片段:

JSONArray results = returnVo.getData().getJSONArray("apps");
for (Object obj : results) {
    JSONObject jsonObj = (JSONObject) obj;
    // jsonObj.put("partner", JSON.toJSONString(jsonObj.getString("partner")));
	// jsonObj.put("partner", JSON.toJSONString(jsonObj.get("partner")));
    // jsonObj.put("partner", jsonObj.get("partner").toString());
}
List<JSONObject> allList = results.toJavaList(JSONObject.class);
List<JSONObject> insertList = allList.stream().filter(x -> x.getLong("id") == null).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(insertList)) {
    channelTiktokAppMapper.insertBatchChannelTiktokApp(insertList);
}

上述代码直接执行,报错信息为(当然,报错信息很大一长串,提取出最有价值的片段):

fix Type handler was null on parameter mapping for property '__frch_params_0.partner'.
It was either not specified and/or could not be found for the javaType (com.alibaba.fastjson.JSONArray) : jdbcType (null) combination.

其中引号内提到partner字段!通过调试,发现
在这里插入图片描述
partner是一个JSONObject!!解决方法,很简单,就是采用注释的代码片段即可。
在这里插入图片描述
其中注释的第二行和第三行等价:
在这里插入图片描述
建议使用第2或3行,不要使用第1行。因为第1行相当于对JSON数据做了一层转义,后续如果需要使用该表字段时,如果是前端,还需要反转义2次

{
    title: '第三方合作伙伴',
    dataIndex: 'partner',
    render: (text) => {
        if (text === "" || text == null) {
            return null;
        }
        return JSON.parse(JSON.parse(text)).partner_name;
    }
}

参考

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: serializerfeature.prettyformat是一个Fastjson序列化特性,用于在序列化JSON时输出格式化的字符串,使其易于阅读和调试。当该特性启用时,Fastjson会在输出的JSON字符串中添加缩进和换行符,以便更好地组织和显示JSON数据。 ### 回答2: serializerfeature.prettyformat 是fastjson 序列化库提供的一个特性,用于在将 Java 对象序列化为 JSON 字符串时,使得生成的 JSON 字符串以更加易读的格式展示。 当我们在使用 fastjson 进行对象转换时,默认生成的 JSON 字符串是紧凑的,即没有任何空格、缩进或换行符。这样的格式虽然在机器读取时没有问题,但对于人类来说阅读起来却很困难。 通过启用 serializerfeature.prettyformat 特性,我们可以让生成的 JSON 字符串具有良好的格式,方便我们排版和阅读。它会给生成的 JSON 字符串添加缩进和换行符,使得属性之间有一定的空格隔开,整个字符串看起来更加美观。 例如,将一个 Java 对象序列化为 JSON 字符串时,默认格式生成如下: {"name":"Tom","age":20,"gender":"male"} 而启用 prettyformat 特性后,生成的 JSON 字符串将会格式化如下: { "name": "Tom", "age": 20, "gender": "male" } 可以看到,每个属性都占据一行,属性名和属性值之间有一定的空格,整个 JSON 字符串更加易读。 值得注意的是,prettyformat 特性对于生成的 JSON 字符串长度会较之前增长,因此在网络传输中需要权衡数据大小和可读性之间的平衡。 ### 回答3: SerializerFeature.PrettyFormat是fastjson提供的一个序列化特性,在序列化JSON时可以将JSON格式化输出,使其更具可读性。 使用PrettyFormat将会使生成的JSON字符串具有良好的缩进和换行格式,以便于阅读和调试。这样可以方便开发人员查看JSON的结构和内容,更加清晰地理解JSON数据。 例如,对于一个简单的JSON对象: { "name": "张三", "age": 20, "address": "上海" } 如果不使用PrettyFormat特性,那么序列化后得到的JSON字符串可能是这样的: {"name":"张三","age":20,"address":"上海"} 而如果使用了PrettyFormat特性,那么序列化后得到的JSON字符串将会是这样的: { "name": "张三", "age": 20, "address": "上海" } 可以看到,使用PrettyFormat后,JSON字符串具有更好的可读性,每个键值对都会占据一行,并且有缩进,使得JSON的结构更加清晰明了。 使用PrettyFormat特性需要注意的是,在生产环境中由于生成的JSON字符串比较冗余,会增加数据传输的大小,因此不应该在生产环境中频繁使用。通常在开发和调试阶段使用PrettyFormat特性可以提高开发效率和调试方便性。 综上所述,SerializerFeature.PrettyFormat是fastjson中的一个序列化特性,可以将生成的JSON字符串格式化输出,使其更具可读性和可理解性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

johnny233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值