FastJson问题记录
  TEZNKK3IfmPf 2023年11月12日 31 0

作为一个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

FastJson问题记录

报错的代码片段(其中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问题记录

 

没道理的啊,重构前没有报错的;最后定位到重构前后使用的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​​字段!通过调试,发现

FastJson问题记录

 

​partner​​是一个JSONObject!!解决方法,很简单,就是采用注释的代码片段即可。

FastJson问题记录

 

其中注释的第二行和第三行等价:

FastJson问题记录

 

建议使用第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;
}
}

参考

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月12日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   2024年05月17日   58   0   0 json
  TEZNKK3IfmPf   2024年04月26日   47   0   0 json
  TEZNKK3IfmPf   2024年04月26日   35   0   0 序列化json
  TEZNKK3IfmPf   2024年04月19日   60   0   0 javajson
  TEZNKK3IfmPf   2024年05月17日   50   0   0 jsonmysql
TEZNKK3IfmPf