通常,fastjson在序列化及反序列化枚举时,一般以下几种策略:
1).根据枚举的name值序列化及反序列化(默认)
2).根据枚举的ordinal序列化及反序列化
3).根据枚举的toString方法序列化,但是反序列仍采取默认的策略
但有时候我们的需求可能不是这样的,比如定义的枚举如下:
public enum DataStatus {
CREATE(1, "新建"),
READ(2, "读取"),
UPDATE(3, "更新"),
DELETE(4, "删除");
private Integer value;
private String text;
DataStatus(Integer value, String text) {
this.value = value;
this.text = text;
}
/*这里省略很多代码*/
public Integer getValue() {
return this.value;
}
public String getText() {
return this.text;
}
@Override
public String toString() {
return "{\"value\":" + this.value + ",text:\"" + this.text + "\"}";
}
}
使用需求是,序列化、反序列化的值是数字,并且在需要的时候,可以显示汉字,因此需要返回一个稍微复杂的json字符串,并提供值和显示文本的内容,由调用者(web端、api调用)决定具体如何使用
通过分析得知,fastjson直接提供的几种策略,都不符合要求,因此我们需要自定义fastjson的序列化、反序列化
fastjson自定义(枚举)序列化、反序列化
实现方式一:Converter、ConverterFactory
参考资料:https://notes.0xl2oot.cn/springboot/2018/11/16/springboot-request-param-enums.html
备 注:最新版本springboot、fastjson未调通,具体原因时间关系,未深入研究~
实现方式二:ObjectSerializer、ObjectDeserializer
参考资料:https://blog.csdn.net/qq_26680031/article/details/83473643
参考资料:https://www.cnblogs.com/insaneXs/p/9515803.html
备 注:调通了,但结合使用需求的过程有些曲折,详见后续内容~
爬坑记录
1、JSONObject的toJavaObjec()反序列化方法不支持注解方式自定义反序列化
枚举类上使用@JSONType(deserializer = xxx.class)
枚举属性类上使用@JSONField(deserializeUsing=xxx.class)
以上两种方式测试都无效,还是调用fastjson默认实现,具体原因是什么,看fastjson实现就知道了
核心实现代码:https://github.com/alibaba/fastjson/blob/master/src/main/java/com/alibaba/fastjson/util/TypeUtils.java
public static T castToEnum(Object obj, Class clazz, ParserConfig mapping){
try{
if(obj instanceof String){
String name = (String) obj;
if(name.length() == 0){
return null;
}
if (mapping == null) {
mapping = ParserConfig.getGlobalInstance();
}
ObjectDeserializer derializer = mapping.getDeserializer(clazz);
if (derializer instanceof EnumDeserializer) {
EnumDeserializer enumDeserializer = (EnumDeserializer) derializer;
return (T) enumDeserializer.getEnumByHashCode(TypeUtils.fnv1a_64(name));
}
return (T) Enum.valueOf((Class extends Enum>) clazz, name);
}
if(obj instanceof BigDecimal){
int ordinal = intValue((BigDecimal) obj);
Object[] values = clazz.getEnumConstants();
if(ordinal < values.length){
return (T) values[ordinal];
}
}
if(obj instanceof Number){
int ordinal = ((Number) obj).intValue();
Object[] values = clazz.getEnumConstants();
if(ordinal < values.length){
return (T) values[ordinal];
}
}
} catch(Exception ex){
throw new JSONException("can not cast to : " + clazz.getName(), ex);
}
throw new JSONException("can not cast to : " + clazz.getName());
}
以上代码中,BigDecimal和Number都还是使用的ordinal方式
2、JSONObject.parseObject(jsonObject.toJSONString(), xxxClass)支持1中的自定义反序列化调用,最终功能实现
通过爬坑过程可以得知,fastjson对String类型的枚举支持是友好的,而对Integer支持是不友好的
甚至在解决String枚举bug的过程中,并没有解决Integer枚举同样的问题
另外,最新版本增加了BigDecimal的支持,但这么“大”的枚举值场景,真的有使用需求吗?
简单记录一下fastjson使用过程中的一些问题,仅供参考~