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

坑——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;
	@Override
	public 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 {

	@Override
	protected 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);
	}

	@Override
	public <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:

测试结果:


http://www.kler.cn/news/290103.html

相关文章:

  • ElasticSearch-数据建模
  • Go语言 Go程基础
  • rust feature 简介
  • shell 学习笔记:向脚本传递参数
  • Android Camera系列(三):GLSurfaceView+Camera
  • 分类预测|基于灰狼GWO优化BP神经网络的数据分类预测Matlab程序GWO-BP|基于鲸鱼WOA优化BP神经网络的数据分类预测Matlab程序WOA-BP
  • 智能提醒助理系列-基础设施准备
  • getLocation:fail, the permission value is offline verifying
  • Flutter--- 常规知识点
  • Redis从入门到入门(上)
  • springboot党员之家服务系统小程序论文源码调试讲解
  • Python知识点:如何使用Python实现强化学习机器人
  • 单片机与人工智能:融合创新的未来之路
  • 【LVGL- 组 lv_group_t】
  • 代码随想录算法训练营第五十六天 | 图论part06
  • 基于STM32的猫狗宠物喂养系统设计(微信小程序)(215)
  • k8s使用报错
  • JavaScript 作用链
  • [C++11#46](三) 详解lambda | 可变参数模板 | emplace_back | 默认的移动构造
  • RESTful基本要求
  • 【MySQL 15】使用 C/C++ 连接数据库
  • 电商行业如何借助BI工具重构“人、货、场”,实现数字化转型?
  • Win32绕过UAC弹窗获取管理员权限
  • 网络安全 加密编码进制
  • 网络安全硬件
  • 区块链Hyperledger Fabric2.2 环境搭建
  • IPSec 参数
  • LeetCode 热题100-64 搜索二维矩阵
  • Spark的Web界面
  • 20240831-PostgreSQL小课持续更新