当前位置: 首页 > news >正文

坑——fastjson将字符串转到带枚举的java对象

fastjson将不同的字符串转换成带枚举的java对象时,不同的字符串值转换成java对象的结果不同;

测试用fastjson版本:

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version>1.2.83</version>

</dependency>

 测试用的java对象:

package test;public enum E {AAA,BBB;
}
package test;public class A {public String abc;public E e;@Overridepublic String toString() {return "A [abc=" + abc + ", e=" + e + "]";}}

测试类:

package test;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;public class Test {public static void main(String[] args) {System.out.println("start");String s = "{\"abc\":\"TEST\",\"e\":\"AAA\"}";JSONObject jobj = JSON.parseObject(s);System.out.println(jobj.get("e"));		A a = JSON.toJavaObject(jobj, A.class);System.out.println(a);}}

正常使用枚举的值字符串状态可以正常转换:

使用枚举中的数值也可以正常转换:

例如使用数值1,String s = "{\"abc\":\"TEST\",\"e\":1}";

但是使用枚举中不存在的字符串,就会返回null,没有该枚举的值也是null:

字符串改为,String s = "{\"abc\":\"TEST\",\"e\":\"XXXXXX\"}";

改为,String s = "{\"abc\":\"TEST\"}";

如果使用枚举中不存在的数值,则会直接抛出异常:

字符串改为,String s = "{\"abc\":\"TEST\",\"e\":100}";

改成对象也是一样的错误:

String s = "{\"abc\":\"TEST\",\"e\":{}}";

String s = "{\"abc\":\"TEST\",\"e\":{\"e\":1}}";

可以看到,不管何种情况,JSONObject都会获得对应的值,但是转换成java对应的对象时就会有不同结果,主要注意这个地方,尤其字符串来自未知的地方;

简单解决,给java对象里的枚举设定默认值,但也只能处理字符串里没有该枚举字段的情况,其他情况还是不行;

彻底解决方法,继承com.alibaba.fastjson.parser.ParserConfig类,重写protected ObjectDeserializer getEnumDeserializer(Class<?> clazz)方法,该方法返回一个com.alibaba.fastjson.parser.deserializer.EnumDeserializer实例,所以还得写个类继承EnumDeserializer类,重写public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName)方法,该方法就是处理各种枚举值转化的地方,可以按照自己的逻辑重写;

源码里getEnumDeserializer()方法:

源码里deserialze()方法:

测试代码:

继承ParserConfig类重写getEnumDeserializer()方法(当然也可以根据clazz处理特定枚举):

package test;import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;public class MyParseConf extends ParserConfig {@Overrideprotected ObjectDeserializer getEnumDeserializer(Class<?> clazz) {System.out.println("MyParseConf clazz:" + clazz);return new MyEnumDeserializer(clazz);}
}

继承EnumDeserializer类重写deserialze()方法,(比如我要处理int值非法,所有非法值默认成0,当然也可以根据type、fieldName参数设置别的值,其他不需要处理的直接跟源码一样就好):

package test;import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;import java.lang.reflect.Type;import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.parser.deserializer.EnumDeserializer;public class MyEnumDeserializer extends EnumDeserializer {public MyEnumDeserializer(Class<?> enumClass) {super(enumClass);}@Overridepublic <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {try {Object value;final JSONLexer lexer = parser.lexer;final int token = lexer.token();if (token == JSONToken.LITERAL_INT) {int intValue = lexer.intValue();lexer.nextToken(JSONToken.COMMA);if (intValue < 0 || intValue >= ordinalEnums.length) {
//                    throw new JSONException("parse enum " + enumClass.getName() + " error, value : " + intValue);System.out.println("deserialze type:" + type);System.out.println("deserialze fName:" + fieldName);intValue = 0;}return (T) ordinalEnums[intValue];} else if (token == JSONToken.LITERAL_STRING) {String name = lexer.stringVal();lexer.nextToken(JSONToken.COMMA);if (name.length() == 0) {return (T) null;}long hash = fnv1a_64_magic_hashcode;long hash_lower = fnv1a_64_magic_hashcode;for (int j = 0; j < name.length(); ++j) {char ch = name.charAt(j);hash ^= ch;hash_lower ^= ((ch >= 'A' && ch <= 'Z') ? (ch + 32) : ch);hash *= fnv1a_64_magic_prime;hash_lower *= fnv1a_64_magic_prime;}Enum e = getEnumByHashCode(hash);if (e == null && hash_lower != hash) {e = getEnumByHashCode(hash_lower);}if (e == null && lexer.isEnabled(Feature.ErrorOnEnumNotMatch)) {throw new JSONException("not match enum value, " + enumClass.getName() + " : " + name);}return (T) e;} else if (token == JSONToken.NULL) {value = null;lexer.nextToken(JSONToken.COMMA);return null;} else {value = parser.parse();}throw new JSONException("parse enum " + enumClass.getName() + " error, value : " + value);} catch (JSONException e) {throw e;} catch (Exception e) {throw new JSONException(e.getMessage(), e);}}}

测试主类,使用自己的ParseConfig:

测试结果:

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【多线程】阻塞,忙等待,睡眠,挂起的简单理解,以及各自优缺点
  • Spring框架——springweb(一篇包会)
  • C#——XML序列化
  • 网络安全服务基础Windows--第9节-DNS部署与安全
  • 使用卫星仿真软件STK的一些应用和思考(星地链路、星间链路)
  • Java JVM 垃圾回收算法详解
  • JS基础学习笔记
  • JAVA毕业设计168—基于Java+Springboot+vue3的高校就业管理系统(源代码+数据库+14000字论文+开题+PPT)
  • 数据结构(1)
  • shell脚本编写之流程控制
  • 《NLP自然语言处理》—— 关键字提取之TF-IDF算法
  • 单片机开发过程中经常会遇到什么问题?
  • 幼儿园数字化探索:从入园适应到全面启智
  • 电镀车间氢气浓度在线监测:智能预警,守护生产安全线
  • 【数据集】RadioML2016.10a(RML2016.10a)
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Docker入门(二) - Dockerfile
  • Terraform入门 - 1. 安装Terraform
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • 第2章 网络文档
  • 简单实现一个textarea自适应高度
  • 判断客户端类型,Android,iOS,PC
  • 微服务框架lagom
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 异常机制详解
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • #QT(一种朴素的计算器实现方法)
  • #在 README.md 中生成项目目录结构
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (二刷)代码随想录第15天|层序遍历 226.翻转二叉树 101.对称二叉树2
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (十七)Flink 容错机制
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (转载)PyTorch代码规范最佳实践和样式指南
  • **PHP分步表单提交思路(分页表单提交)
  • .NET MAUI Sqlite数据库操作(二)异步初始化方法
  • .net 调用php,php 调用.net com组件 --
  • .NET关于 跳过SSL中遇到的问题
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • 。。。。。
  • @Transient注解
  • [ vulhub漏洞复现篇 ] GhostScript 沙箱绕过(任意命令执行)漏洞CVE-2019-6116
  • [240527] 谷歌 CEO 承认 AI 编造虚假信息问题难解(此文使用 @gemini 命令二次创作)| ICQ 停止运作
  • [acwing周赛复盘] 第 94 场周赛20230311
  • [Algorithm][综合训练][体育课测验(二)][合唱队形][宵暗的妖怪]详细讲解
  • [Android Studio 权威教程]断点调试和高级调试
  • [Android]如何调试Native memory crash issue
  • [APIO2012] 派遣 dispatching
  • [APIO2015]巴厘岛的雕塑
  • [C#]使用PaddleInference图片旋转四种角度检测
  • [C++数据结构](22)哈希表与unordered_set,unordered_map实现
  • [hive小技巧]同一份数据多种处理