Gson 对enum的序列化与反序列化配置
前言
在网上找了一圈,都没有一个能够直接配置使用的Gson序列化enum配置。
对于enum序列化与反序列化的主要难点在于TypeAdapter在反序列化时无法获取enum的类型。
基础序列化与反序列化(仅使用enum的ordinal与name)
此处提供的解决方案可以通过闭包的方式从TypeAdapterFactory实例中获取enum的class,在此放出一份最简单的增强Gson序列化与反序列化enum的方法。
(可选则将enum序列化为enum序号或名字,也可选通过enum的序号或名字或两者反序列化enum)
/**
* 使用配置生成Gson对象
* @author lty2008one
* @date 2022/02/20
*/
private static Gson configure() {
return new GsonBuilder()
.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> token) {
Class<? super T> rawType = token.getRawType();
if(rawType.isEnum()) return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) {
if(value == null) out.nullValue();
// 此处定义序列化为枚举的序号,也可换成name()序列化为名字,或自定义喜欢的格式
else out.value(((Enum<T>) value).ordinal());
}
@Override
public T read(JsonReader in) throws IOException {
if(in.peek().equals(JsonToken.NULL)) {
in.nextNull();
return null;
} else {
String value = in.nextString();
try {
Method values = type.getRawType().getMethod("values");
Enum[] invoke = (SerializableEnum[]) values.invoke(type.getClass());
for(int i = 0; i < invoke.length; i++) {
// 通过序号反序列化
if(Objects.equals(invoke[i].ordinal()+"", value)) return invoke[i];
// 通过名字反序列化
if(Objects.equals(invoke[i].name(), value)) return invoke[i];
}
} catch (Exception e) {
e.printStackTrace();
} return null;
}
}
} else return null;
}
}).create();
}
经过此配置后,Gson就可以序列化Enum为ordinal,也可以通过Enum的名字和序号反序列化Enum而非必须使用名字进行反序列化了
进阶序列化与反序列化(任意配置,可继承)
当然,对于大家来说,只做到这一层肯定还不太够用,我们可能需要的是针对一些特殊的Enum有各自的序列化方法,而且不用将每个Enum类都注册到TypeAdapter中
那么我们首先定义一个Enum的通用接口
/**
* 实现此接口的必须为enum,否则可能会报错
* 实现时,泛型E必须为实现类的类型
* @author lty2008one
* @date 2022/02/20
*/
public interface SerializableEnum<E> {
/**
* 默认将enum序列化为序号
* @return
*/
default String serializenum() {
return ((Enum) this).ordinal() + "";
}
/**
* 默认使用序号和名字进行反序列化
* @param value
* @return
*/
default E deserializenum(String value) {
if(value == null) return null;
Class<? extends SerializableEnum> clazz = getClass();
for (SerializableEnum enumConstant : clazz.getEnumConstants()) {
Enum enumValue = (Enum) enumConstant;
if(value.equals(enumValue.ordinal() + "")) return (E) enumConstant;
if(value.equals(enumValue.name())) return (E) enumConstant;
} return null;
}
}
有这个通用接口在手,我们就可以对序列化与反序列化的方法进行严格管控了,此时,在Gson的配置项中如此注册即可。
private static Gson config() {
List<String> allow = Arrays.asList("serializenum", "deserializenum");
return new GsonBuilder()
.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<? super T> rawType = type.getRawType();
if (rawType.isEnum()) {
boolean pass = Stream.of(rawType.getMethods()).map(Method::getName)
.collect(Collectors.toList()).containsAll(allow);
if (pass) return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
if(value == null) out.nullValue();
else out.value(((SerializableEnum<T>) value).serializenum());
}
@Override
public T read(JsonReader in) throws IOException {
if(in.peek().equals(JsonToken.NULL)) {
in.nextNull();
return null;
} else {
String value = in.nextString();
try {
Method values = type.getRawType().getMethod("values");
SerializableEnum[] invoke = (SerializableEnum[]) values.invoke(type.getClass());
return (T) invoke[0].deserializenum(value);
} catch (Exception e) {
e.printStackTrace();
} return null;
}
}
};
} return null;
}
}).create();
}
此时,在任一实现了SerializableEnum接口的enum枚举类中重新实现serializenum和deserializenum方法即可快速更改此枚举的序列化与反序列化方式
例如
public enum Sex implements SerializableEnum<Sex> {
FUTA(2, "扶她"),
FEMALE(0, "女"),
OTHER(3, "其他"),
MALE(1, "男");
private final int order;
private final String desc;
Sex(int order, String desc) {
this.order = order;
this.desc = desc;
}
@Override
public String serializenum() {
// 序列化为描述
return desc;
}
@Override
public Sex deserializenum(String value) {
for (Sex sex : values()) {
// 仅使用自定义的order值进行反序列化
if(Objects.equals(sex.order + "", value)) return sex;
} return null;
}
}
在序列化时会被序列化为描述,反序列化时则会根据定义的order值找到对应的项
欢迎各位学习使用,转载或引用请附带原文发布连接