基于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":"处理中"}}
}