在java或各种框架中,[反]序列化对枚举类型默认一般都是用的枚举的名称。
如果代码有魔法值,我们想改成枚举类型。但我们在改造、重构后端的时候不想对前端或者第三方有影响,可以用到此方法。

必须用到的枚举工具类

  • 参考通用枚举 通用枚举
    所有的枚举想要实现(反)序列化比较要用到通用枚举,实现通用枚举的接口即可

代码(最初版本)

1.可反序列化的枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* 可反序列化的枚举
*/
public enum DeserializableEnum {
// DBEnum是MybatisPlus枚举的adapter,增加了getDoc的方法。MybatisPlus是直接支持数据库[反]序列化的。但是还不支持和前端的[反]序列化的交互,所以在此配置即可。
DB_Enum(DBEnum.class, DBEnum::getValue, DB_Enum::getDoc),
// 一个枚举对应单个标识
I_Enum(IEnum.class, IEnum::getIdentity, IEnum::getDoc),
// 一个枚举对应多个标识
I_Enums(IEnums.class, IEnums::getIdentities, IEnums::getDoc),
;

private final Class<?> enumInterface;
private final Function<Object, Serializable> getIdentityFunction;
private final Function<Object, String> getDocFunction;

<T> DeserializableEnum(Class<T> enumInterface, Function<T, Serializable> getIdentityFunction, Function<T, String> getDocFunction) {
this.enumInterface = enumInterface;
this.getIdentityFunction = (Function<Object, Serializable>) getIdentityFunction;
this.getDocFunction = (Function<Object, String>) getDocFunction;
}

/**
* 判断class是否可以进行反序列化
*/
public static Optional<DeserializableEnum> getDeserializableEnum(Class<?> enumClass) {
if (enumClass == null) {
return Optional.empty();
}
if (!enumClass.isEnum()) {
return Optional.empty();
}
for (DeserializableEnum deserializableEnum : DeserializableEnum.values()) {
if (deserializableEnum.enumInterface.isAssignableFrom(enumClass)) {
return Optional.of(deserializableEnum);
}
}
return Optional.empty();
}

/**
* 进行反序列化
*/
private Enum<?> deserializeEnum(Class<Enum<?>> enumClass, Object rawValue) {
for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
Serializable identity = this.getGetIdentityFunction.apply(enumConstant);
if (Objects.equals(identity, rawValue)) {
return enumConstant;
}
if (identity instanceof Object[]) {
for (Object i : (Object[]) identity) {
if (Objects.equals(i, rawValue)) {
return enumConstant;
}
}
}
}

// 最后值为空则返回null
if (rawValue == null || StringUtils.isBlank(rawValue.toString())) {
return null;
}

// 转换失败一律抛异常,后期可根据注解进行动态的抛异常
throw new EnumDeserializeException(MessageFormat.format("参数错误,无法匹配对应的类型,value:{0}, type:{1}", rawValue, enumClass.getSimpleName()));
}
}

2.枚举[反]序列化的各种框架配置 如jackson,fastjson,springConvert等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
public class EnumDeserializerImpls {
/**
* 定义一个常量为spring converter 组件
*/
public static final GenericConverter ENUM_DESERIALIZER_CONVERTER = new GenericConverter() {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
Set<ConvertiblePair> cpSet = Sets.newHashSet();
for (DeserializableEnum deserializableEnum : DeserializableEnum.values()) {
/* convert匹配逻辑:
先循环 sourceType 水平class
然后内嵌 targetType 水平class 进行内嵌循环
找对对应的ConvertiblePair为止
所以sourceType必须比较精确不然覆盖不了defaultConvertor
*/
cpSet.add(new ConvertiblePair(String.class, deserializableEnum.enumInterface));
cpSet.add(new ConvertiblePair(Number.class, deserializableEnum.enumInterface));
}
return cpSet;
}

@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
ResolvableType targetResolvableType = targetType.getResolvableType();
Class<?> valueRawClass = getValueRawClass(targetResolvableType);
if (valueRawClass == null) {
return source;
}
Class<Enum<?>> enumClass = (Class<Enum<?>>) targetResolvableType.resolve();

DeserializableEnum deserializableEnum = DeserializableEnum.getDeserializableEnum(enumClass).orElse(null);
if (deserializableEnum == null) {
return source;
}
// 把source转换成枚举真实值的类型
Object rawValue = DefaultConversionService.getSharedInstance().convert(source, valueRawClass);
return deserializableEnum.deserializeEnum(enumClass, rawValue);
}
};

/**
* 定义一个常量为jacksonModule 组件
*/
public static final SimpleModule ENUM_MODULE = new SimpleModule() {
@Override
public void setupModule(SetupContext context) {

// 添加枚举反序列化
context.addDeserializers(new Deserializers.Base() {
@Override
public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc) {
Class<?> valueRawClass = getValueRawClass(ResolvableType.forClass(type));
if (valueRawClass == null) {
return null;
}

return DeserializableEnum.getDeserializableEnum(type)
.map(deserializableEnum -> new JsonDeserializer<Enum<?>>() {
@Override
public Enum<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
Object value = DefaultConversionService.getSharedInstance().convert(jsonParser.getValueAsString(), valueRawClass);
return deserializableEnum.deserializeEnum((Class<Enum<?>>) type, value);
}
}).orElse(null);

}
});
}

};


/*
* 定义一个常量为fastJson 组件
*/
public static final Module FASTJSON_MODULE = new Module() {
@Override
public ObjectDeserializer createDeserializer(ParserConfig config, Class type) {
Class<?> valueRawClass = getValueRawClass(ResolvableType.forClass(type));
if (valueRawClass == null) {
return null;
}

return DeserializableEnum.getDeserializableEnum(type)
.map(deserializableEnum -> new ObjectDeserializer() {
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
Object value = parser.parse();

Object rawValue = DefaultConversionService.getSharedInstance().convert(value, valueRawClass);
return (T) deserializableEnum.deserializeEnum((Class<Enum<?>>) type, rawValue);
}

public int getFastMatchToken() {
return JSONToken.LITERAL_STRING;
}
}).orElse(null);

}

@Override
public ObjectSerializer createSerializer(SerializeConfig config, Class type) {
return null;
}
};

/**
* 获取接口上的泛型
*/
public static Class<?> getValueRawClass(ResolvableType realClassResolvedType) {
ResolvableType[] enumInterfaces = realClassResolvedType.getInterfaces();
if (ArrayUtils.isEmpty(enumInterfaces)) {
return null;
}
ResolvableType valueResolvableType = enumInterfaces[0].getGeneric(0);
if (valueResolvableType == ResolvableType.NONE) {
return null;
}
return valueResolvableType.getRawClass();
}

}

缺陷

1:目前不支持序列化😝
2:获取枚举identity类型的方法比较愚钝,一刀切了

如果是间接实现的枚举或者泛型的位置不在第一个位置,那就有问题喽。

3:反序列化和序列化只能单层序列和反序列,什么意思呢?

枚举的标识可以获取到枚举对吧?如果枚举的标识还是一个枚举呢?目前只支持序列化和反序列化最外一层的value。太抽象?

  • 用户类型1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public enum UserType1 implements IEnum<Integer> {

    NEW_USER(1, "新用户"),

    OLD_USER(0, "老用户");
    public final int code;

    public final String doc;

    UserType1(int code, String doc) {
    this.code = code;
    this.doc = doc;
    }

    @Override
    public Integer getIdentity() {
    return code;
    }
    }
  • 用户类型2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public enum UserType2 implements IEnum<UserType1> {

    NEW_USER(UserType1.NEW_USER, "新用户2"),

    OLD_USER(UserType1.OLD_USER, "旧用户2");

    public final UserType1 userType1;

    public final String doc;

    UserType2(UserType1 userType1, String doc) {
    this.userType1 = userType1;
    this.doc = doc;
    }

    /**
    * 当前枚举的标识是userType1枚举类型
    */
    @Override
    public UserType1 getIdentity() {
    return userType1;
    }

    @Override
    public String getDoc() {
    return doc;
    }
    }

    如果用UserType2枚举的话,反序列化或者序列化就会出现缺陷。因为不支持内嵌套一个枚举当做当前枚举的标识。
    所以有了以下的新代码。

代码(新版本)

  • 支持序列化啦
  • 支持枚举内嵌的标识序列化和反序列化
  • 更精准的获取泛型上的标识

1.可反序列化的枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* 可反序列化的枚举
*/
public enum DeserializableEnum {
// DBEnum是MybatisPlus枚举的adapter,增加了getDoc的方法。MybatisPlus是直接支持数据库[反]序列化的。但是还不支持和前端的[反]序列化的交互,所以在此配置即可。
DB_Enum(DBEnum.class, 0, DBEnum::getValue, DBEnum::getDoc),
I_Enum(IEnum.class, 0, IEnum::getIdentity, IEnum::getDoc),
I_Enums(IEnums.class, 0, IEnums::getIdentities, IEnums::getDoc),
;

// 可反序列化枚举的接口
public final Class<?> enumInterface;
// 接口对应的identity(枚举的标识)泛型的下标
private final int identityGenericIndex;
// 获取枚举的identity
private final Function<Enum<?>, Serializable> getIdentityFunction;
// 获取枚举的doc(文档)
public final Function<Enum<?>, String> getDocFunction;

<T> DeserializableEnum(Class<T> enumInterface, int identityGenericIndex, Function<T, Serializable> getIdentityFunction, Function<T, String> getDocFunction) {
this.enumInterface = enumInterface;
this.identityGenericIndex = identityGenericIndex;
this.getIdentityFunction = (Function<Enum<?>, Serializable>) getIdentityFunction;
this.getDocFunction = (Function<Enum<?>, String>) getDocFunction;
}

/**
* 进行反序列化
*/
private Enum<?> deserializeEnum(Class<Enum<?>> enumClass, Object rawValue) {
for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
Object identity = getIdentity(enumConstant);
if (identity instanceof Object[]) {
for (Object i : (Object[]) identity) {
if (Objects.equals(i, rawValue)) {
return enumConstant;
}
}
} else {
if (Objects.equals(identity, rawValue)) {
return enumConstant;
}
}
}

// 最后值为空则返回null
if (rawValue == null || StringUtils.isBlank(rawValue.toString())) {
return null;
}

// 转换失败一律抛异常,后期可根据注解进行动态的抛异常
throw new EnumDeserializeException(MessageFormat.format("参数错误,无法匹配对应的类型,value:{0}, type:{1}", rawValue, enumClass.getSimpleName()));
}

public Object getIdentity(Enum<?> enumConstant) {
Serializable identity = this.getIdentityFunction.apply(enumConstant);
if (identity == null) {
return null;
}
Class<?> identityType;
if (identity instanceof Object[]) {
identityType = identity.getClass().getComponentType();
} else {
identityType = identity.getClass();
}

// 递归判断
DeserializableEnum deserializableEnum = getDeserializableEnumAndIdentityClass(identityType)
.map(Map.Entry::getKey)
.orElse(null);

if (deserializableEnum == null || !(identity instanceof Enum<?>)) {
return identity;
}
return deserializableEnum.getIdentity((Enum<?>) identity);
}

/**
* 获取接口上标识枚举值的类型
*/
private Class<?> getValueRawClass(Class<?> enumInterface) {
ResolvableType resolvableType = ResolvableType.forClass(enumInterface).as(this.enumInterface);
Class<?> valueRawClass = resolvableType.getGeneric(this.identityGenericIndex).resolve();
// 判断递归获取
DeserializableEnum deserializableEnum = DeserializableEnum.getDeserializableEnumAndIdentityClass(valueRawClass)
.map(Map.Entry::getKey)
.orElse(null);

if (deserializableEnum == null) {
return valueRawClass;
}

return deserializableEnum.getValueRawClass(valueRawClass);
}

public static Optional<Map.Entry<DeserializableEnum, Class<?>>> getDeserializableEnumAndIdentityClass(Class<?> enumClass) {
if (enumClass == null) {
return Optional.empty();
}
if (!enumClass.isEnum()) {
return Optional.empty();
}
for (DeserializableEnum deserializableEnum : DeserializableEnum.values()) {
if (deserializableEnum.enumInterface.isAssignableFrom(enumClass)) {
Class<?> identityValueRawClass = deserializableEnum.getValueRawClass(enumClass);
return Optional.of(Pair.of(deserializableEnum, identityValueRawClass));
}
}
return Optional.empty();
}
}

2.枚举[反]序列化的各种框架配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
@Slf4j
public class EnumDeserializerImpls {
/**
* spring converter反序列化
*/
public static final GenericConverter ENUM_DESERIALIZER_CONVERTER = new GenericConverter() {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
Set<ConvertiblePair> cpSet = Sets.newHashSet();
for (DeserializableEnum deserializableEnum : DeserializableEnum.values()) {
/* 匹配逻辑:
先循环 sourceType 水平class
然后内嵌 targetType 水平class 进行内嵌循环
找对对应的ConvertiblePair为止
所以sourceType必须比较精确不然覆盖不了defaultConvertor
*/
cpSet.add(new ConvertiblePair(String.class, deserializableEnum.enumInterface));
cpSet.add(new ConvertiblePair(Number.class, deserializableEnum.enumInterface));
}
return cpSet;
}

@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> targetClass = targetType.getResolvableType().resolve();

return DeserializableEnum.getDeserializableEnumAndIdentityClass(targetClass)
.map((Function<Map.Entry<DeserializableEnum, Class<?>>, Object>) entry -> {
Object rawValue = DefaultConversionService.getSharedInstance().convert(source, entry.getValue());
return entry.getKey().deserializeEnum((Class<Enum<?>>) targetClass, rawValue);
}).orElse(null);
}
};

/**
* jacksonModule序列化和反序列化
*/
public static final SimpleModule ENUM_MODULE = new SimpleModule() {
@Override
public void setupModule(SetupContext context) {
context.addSerializers(new Serializers.Base() {
@Override
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
return DeserializableEnum.getDeserializableEnumAndIdentityClass(type.getRawClass())
.map(Map.Entry::getKey)
.map(deserializableEnum -> new JsonSerializer<Enum<?>>() {
@Override
public void serialize(Enum<?> o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeObject(deserializableEnum.getIdentity(o));
}
}).orElse(null);
}
});

context.addDeserializers(new Deserializers.Base() {
@Override
public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc) {
return DeserializableEnum.getDeserializableEnumAndIdentityClass(type)
.map(entry -> new JsonDeserializer<Enum<?>>() {
public Enum<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
Object value = DefaultConversionService.getSharedInstance().convert(jsonParser.getValueAsString(), entry.getValue());
return entry.getKey().deserializeEnum((Class<Enum<?>>) type, value);
}
}).orElse(null);
}
});
}

};


/*
* fastJson 枚举序列化和反序列化
*/
public static final Module FASTJSON_MODULE = new Module() {
@Override
public ObjectDeserializer createDeserializer(ParserConfig config, Class type) {
return DeserializableEnum.getDeserializableEnumAndIdentityClass(type)
.map(entry -> new ObjectDeserializer() {
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
Object rawValue = DefaultConversionService.getSharedInstance().convert(parser.parse(), entry.getValue());
return (T) entry.getKey().deserializeEnum((Class<Enum<?>>) type, rawValue);
}

@Override
public int getFastMatchToken() {
return JSONToken.LITERAL_STRING;
}
}).orElse(null);
}

@Override
public ObjectSerializer createSerializer(SerializeConfig config, Class type) {
return DeserializableEnum.getDeserializableEnumAndIdentityClass(type)
.map(entry -> new ObjectSerializer() {
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
serializer.write(entry.getKey().getIdentity((Enum<?>) object));
}
}).orElse(null);
}
};

}

基本实现以及各种框架的组件都已经定义出来了,可以参考springMvc枚举序列化和反序列化,把这些实现应用到实际的项目当中。
然后我们用各种枚举什么的,后端可以安心的进行重构(使用)了

新代码用到了递归,如果不理解旧代码的话阅读起来比较复杂。建议先理解旧代码在阅读新代码