gson自定义枚举类型序列化反序列化

基于gson,自定义枚举类型序列化反序列化,实际效果如下:

实体类:OrderDO:{orderNo="202109050001", status=StatusEnum.PENDING

json串:{"orderNo":"202109050001","status":{"value":2,"desc":"处理中"}}

二者互相转换

需求背景:将枚举元素序列化为带有值和说明(value/desc)的json串(json接收方就不需要自己翻译了),反序列化则基于值(value)进行匹配


问题:由于gson的JsonSerializer、JsonDeserializer只支持单个具体类型,如果有多个enum类型,就得实现多个JsonSerializer、JsonDeserializer,并注册多次,只能寻找其他解决方式


解决方式:gson的TypeAdapterFactory支持根据对象类型动态返回一个TypeAdapter,执行序列化与反序列化,正好适用,步骤如下(建议读者直接按顺序copy到自己工程下):

0、准备枚举类通用接口BasicEnum、以及一个用于测试的枚举类、和一个实体类

public interface BasicEnum<V> {
    V getValue();
    String getDesc();
}

public enum StatusEnum implements BasicEnum<Integer> {
    INIT(1,"初始化"),
    PENDING(2,"处理中"),
    FINISHED(3,"完成")
    ;
    private Integer value;
    private String desc;

    StatusEnum(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    @Override
    public Integer getValue() {
        return value;
    }

    @Override
    public String getDesc() {
        return desc;
    }
}

public class OrderDO {
    private String orderNo;
    private StatusEnum status;

    public OrderDO() {
    }

    public OrderDO(String orderNo, StatusEnum status) {
        this.orderNo = orderNo;
        this.status = status;
    }

    public String getOrderNo() {
        return orderNo;
    }

    public StatusEnum getStatus() {
        return status;
    }

    @Override
    public String toString() {
        return "OrderDO{" +
                "orderNo='" + orderNo + '\'' +
                ", status=" + status +
                '}';
    }
}

1、自定义BasicEnumTypeAdapter(实现TypeAdapter#write,TypeAdapter#read)
逻辑:将所有实现了BasicEnum的枚举类(如StatusEnum)序列化为类似于{"value":"1","desc":"初始化"}这种包含value、desc的json串)

import com.bobo.json.gson.constant.BasicEnum;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import  com.bobo.json.gson.constant.StatusEnum;
/**
 * @author bobo
 * @date 2021/9/4
 * @desc 将实现了 {@link BasicEnum}的枚举类(如{@link StatusEnum})序列化为类似于{"value":"1","desc":"初始化"}这种包含value、desc的json串
 * @desc 反序列化时,根据json串中的value值匹配对应枚举元素
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class BasicEnumTypeAdapter<T extends BasicEnum> extends TypeAdapter<T> {

    /**
     * 枚举类的所有枚举元素
     */
    private final T[] enumConstants;

    /**
    * 枚举类的value属性类型(当前只支持String、Integer两种value,新增类型需要修改 {@link EnumValueType}、read、write方法)
    */
    private final EnumValueType enumValueType;

    /**
    * @param type 表示当前待序列化/反序列化的对象实际类型
    */
    public BasicEnumTypeAdapter(TypeToken<T> type) {
        final Class<T> actualType = (Class) type.getRawType();
        Type[] genericInterfaceTypes = (Type[]) actualType.getGenericInterfaces();
        Class valueClazz = null;
        for (Type genericInterfaceType : genericInterfaceTypes) {
            if (genericInterfaceType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericInterfaceType;
                Class clazz = (Class) parameterizedType.getRawType();
                if (clazz == BasicEnum.class) {
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    valueClazz = (Class) actualTypeArguments[0];
                }
            }
        }
        if (valueClazz == null) {
            throw new IllegalArgumentException("未获取到枚举value类型");
        }
        if (valueClazz == String.class) {
            enumValueType = EnumValueType.STRING;
        } else if (valueClazz == Integer.class) {
            enumValueType = EnumValueType.INTEGER;
        } else {
            throw new IllegalArgumentException("不支持的枚举value类型");
        }
        enumConstants = actualType.getEnumConstants();
    }

    @Override
    public void write(JsonWriter out, BasicEnum value) throws IOException {
        switch (enumValueType) {
            case STRING:
                out.beginObject()
                        .name("value").value((String) value.getValue())
                        .name("desc").value(value.getDesc())
                        .endObject();
                break;
            case INTEGER:
                out.beginObject()
                        .name("value").value((Integer) value.getValue())
                        .name("desc").value(value.getDesc())
                        .endObject();
                break;
            default:
                throw new IllegalArgumentException(String.format("不支持的枚举value类型:%s", enumValueType));
        }
    }

    @Override
    public T read(JsonReader in) throws IOException {
        JsonElement jsonElement = Streams.parse(in);
        JsonObject jsonObject = jsonElement.getAsJsonObject();
        Object value = null;

        switch (enumValueType) {
            case STRING:
                value = jsonObject.get("value").getAsString();
                break;
            case INTEGER:
                value = jsonObject.get("value").getAsInt();
                break;
            default:
                throw new IllegalArgumentException(String.format("不支持的枚举value类型:%s", enumValueType));
        }

        for (T basicEnum : enumConstants) {
            if (basicEnum.getValue().equals(value)) {
                return basicEnum;
            }
        }
        return null;
    }

    private enum EnumValueType {
        STRING, INTEGER
    }
}

2、自定义BasicEnumTypeAdapterFactory(实现TypeAdapterFactory#create方法)
逻辑:如果枚举类实现了BasicEnum,使用BasicEnumTypeAdapter执行序列化、反序列化 

import com.bobo.json.gson.constant.BasicEnum;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;

/**
* @desc 如果枚举类实现了 {@link BasicEnum},使用 {@link BasicEnumTypeAdapter}执行序列化、反序列化
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class BasicEnumTypeAdapterFactory implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        Class<T> rawType = (Class<T>) type.getRawType();
        if (!rawType.isEnum()) {
            //没有指定的转换器的时候,返回NULL,GSON会自己适配相关的解析器
            return null;
        } else {
            final Class<?>[] interfaces = rawType.getInterfaces();
            for (Class c : interfaces) {
                if (c.equals(BasicEnum.class)) {
                    //type表示当前待序列化/反序列化的对象实际类型
                    return new BasicEnumTypeAdapter(type);
                }
            }
            return null;
        }
    }
}

3、使用GsonBuilder#registerTypeAdapterFactory注册BasicEnumTypeAdapterFactory,并测试序列化、反序列化效果

import com.bobo.json.gson.constant.RoleEnum;
import com.bobo.json.gson.constant.StatusEnum;
import com.bobo.json.gson.custom.BasicEnumTypeAdapterFactory;
import com.bobo.json.gson.entity.OrderDO;
import com.bobo.json.gson.entity.UserDO;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * @author bobo
 * @date 2021/9/5
 */
public class EnumSerializeDeserializeTest {

    public static void main(String[] args) {
        //注册自定义的TypeAdapterFactory
        Gson gson = new GsonBuilder()
                .registerTypeAdapterFactory(new BasicEnumTypeAdapterFactory())
                .create();

        String orderTo=gson.toJson(new OrderDO("202109050001", StatusEnum.PENDING));
        System.out.println(String.format("value为String类型,序列化结果:%s",orderTo));

        OrderDO orderFrom=gson.fromJson(orderTo,OrderDO.class);
        System.out.println(String.format("value为String类型,反序列化结果:%s",orderFrom));
    }

//序列化、反序列化效果:
//实体类:OrderDO{orderNo="202109050001", status=StatusEnum.PENDING} <-->  json串:{"orderNo":"202109050001","status":{"value":2,"desc":"处理中"}}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值