fastjson实现自定义枚举序列化与反序列化

2,015 阅读2分钟

背景

公司项目中使用的fastjson版本是1.2.68,该版本对枚举的序列化和反序列都是根据枚举name来实现的,而实际对外提供的接口中都是定义code值,例如1,2,3,4这样的。如果要实现这个效果要么升级到fastjson2、或者用jackson,但是考虑到是线上项目,升级存在风险比较大,所有改用fastjson自定义序列化和反序列化。

解决方法

1. 定义一个通用枚举接口

public interface MyEnum {
   /*
   * 获取code值方法
   */
   int getCode();
}

2. 自定义枚举序列化和反序列

public class MyEnumCodec implements ObjectSerializer, ObjectDeserializer {

/**
 * 反序列化
*/
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        if (object == null){
            serializer.out.writeNull();
        } else {
            MyEnum myEnum = (MyEnum) object;
            //序列化时调用getCode方法
            serializer.out.writeInt(myEnum.getCode());
        }
    }
    
    /**
     * 反序列化
    */
    @Override
    public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
        final JSONLexer lexer = parser.lexer;
        final int token = lexer.token();
        Class cls = (Class) type;
        //获取当前枚举的所有枚举值
        Object[] enumConstants = cls.getEnumConstants();
        //判断class是不是MyEnum的实现类
        if (MyEnum.class.isAssignableFrom(cls)) {
            for (Object enumConstant : enumConstants) {
                //如果getCode返回值等于json字段的值,返回这个枚举值
                if (((MyEnum) enumConstant).getCode() == lexer.intValue()) {
                    return (T) enumConstant;
                }
            }
        } else {
            //没实现EnumValue接口的 默认的按名字或者按ordinal
            if (token == JSONToken.LITERAL_INT) {
                int intValue = lexer.intValue();
                lexer.nextToken(JSONToken.COMMA);

                if (intValue < 0 || intValue > enumConstants.length) {
                    throw new JSONException("parse enum " + cls.getName() + " error, value : " + intValue);
                }
                return (T) enumConstants[intValue];
            } else if (token == JSONToken.LITERAL_STRING) {
                return (T) Enum.valueOf(cls, lexer.stringVal());
            }
        }
        return null;
    }

    @Override
    public int getFastMatchToken() {
        return JSONToken.LITERAL_INT;
    }
}

3. 自定义枚举类

public interface MySelfEnums {

//这里用到了lombok 默认实现了getCode方法
@Getter
enum Delete implements MyEnum{
    /**
     * 停用
     */
    NO_DELETE(0,"否"),
    /**
     * 启用
     */
    DELETED(1,"是");

    private int code;
    private String desc;

    Delete(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

  
}

4.声明自定义序列化

/**
 * 用fastjson注解声明
 */
@JSONField(serializeUsing = MyEnumCodec.class, deserializeUsing = MyEnumCodec.class)
private MySelfEnums.Delete delete;

考虑到项目中这种自定义序列化和反序列的枚举很多,使用全局配置比较轻松

//获取所有实现MyEnum的子类 这里使用了Hutool工具类
Set<Class<?>> classSet = ClassUtil.scanPackageBySuper("com.mambike.sales", MyEnum.class);
for (Class<?> aClass : classSet) {
    //判断是否为枚举
    if (ClassUtil.isEnum(aClass)){
        //fastjson只有拿到具体类才能配置序列化和反序列成功
        //添加序列化
        SerializeConfig.getGlobalInstance().put(aClass, new MyEnumCodec());
        //添加反序列化
        ParserConfig.getGlobalInstance().putDeserializer(aClass, new MyEnumCodec());
    }
}

项目使用的是springboot,这段代码只要在springboot启用之前执行就可以。

总结

这次实现自定义枚举序列化和反序列参考很多网上其他作者的资料,我只是稍微添加一下自己的思考。希望给遇到这类问题的小伙伴提供一个借鉴的思路。我也只是稍微测试了一下,项目中没什么问题,如果有什么不足之处还望指教。

参考文章

自定义fastjson对枚举类型的序列化及反序列化过程 - 走看看 (zoukankan.com)

www.cxyzjd.com/article/qq_…