-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
findCustomEnumDeserializer by interfaces #2842
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thank you for suggesting this fix. I am not quite sure I understand what problem it would solve -- would it be possible to show a test case? |
when I receive json data from http request body by SpringMVC Controller, Controller below @PostMapping("add/body")
public BaseResponseVO saveBody(@Valid @RequestBody UserParam userParam) {
log.debug("\n====>当前添加的用户信息是{}", userParam);
UserModel userModel = userService.add(userParam);
return BaseResponseVO.success(userModel);
} the Controller need to deserialize the json data to POJO. there is a Field which is Enum type defined by myself in the POJO. POJO below
first , I need to defined a interface which would be implements by Enum.like this package hxy.dream.entity.enums;
/**
* The interface Enumerator.
*/
// 如果发现注入的bean无法解决json序列化问题,那么可以加上这个注解
//@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public interface BaseEnum {
/**
* Code integer.
*
* @return the integer
*/
Integer code();
/**
* Description string.
*
* @return the string
*/
String description();
} so that I defined a Eume name public enum GenderEnum implements BaseEnum {
BOY(100, "男"), GIRL(200, "女"),UNKNOWN(0, "未知");
@EnumValue//标记数据库存的值是code
private final Integer code;
private final String description;
GenderEnum(int code, String description) {
this.code = code;
this.description = description;
}
@Override
public Integer code() {
return code;
}
@Override
public String description() {
return description;
} maybe there are many Enum implement actually, I found @Slf4j
@Configuration
public class BeanConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer enumCustomizer() {
// 将枚举转成json返回给前端
return jacksonObjectMapperBuilder -> {
// 自定义序列化器注入
Map<Class<?>, JsonSerializer<?>> serializers = new LinkedHashMap<>();
serializers.put(BaseEnum.class, new BaseEnumSerializer());
serializers.put(Date.class, new DateJsonSerializer());
jacksonObjectMapperBuilder.serializersByType(serializers);
// 自定义反序列化器注入
Map<Class<?>, JsonDeserializer<?>> deserializers = new LinkedHashMap<>();
deserializers.put(BaseEnum.class, new BaseEnumDeserializer());
// deserializers.put(GenderEnum.class, new BaseEnumDeserializer());
jacksonObjectMapperBuilder.deserializersByType(deserializers);
};
}
} in current jackson code ,if I want to deserialize deserializers.put(GenderEnum.class, new BaseEnumDeserializer()); if there are many Enums implements // 这里应该需要继续查找当前类的父接口
List<JavaType> interfaces = type.getInterfaces();
// 再次查找自定义的序列化器
for (JavaType javaType : interfaces) {
Class<?> rawClass = javaType.getRawClass();
deser = _findCustomEnumDeserializer(rawClass, config, beanDesc);
if (deser != null) {
return deser;
}
} if you have time and interesting to run my project https://github.com/aohanhongzhi/SpringCloud-multiple-gradle , it's my honor to show the feature to complete Enum deserialize by |
@aohanhongzhi First of all, thank you for full explanation -- this makes sense. Now, the challenge here is that deserializer registration relies on class(es) from Spring framework, So it is up to |
@cowtowncoder In the current version, how can I implement the enumeration deserialization I want |
@aohanhongzhi First you need to ensure your deserializer is used by Spring: I do not know exactly how that is to be done. But as I said, you need to somehow register
and you may need to implement both to see if type given should be handled by your deserializer. |
@cowtowncoder protected JsonDeserializer<?> _createDeserializer2(DeserializationContext ctxt,
DeserializerFactory factory, JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
// If not, let's see which factory method to use
// 12-Feb-20202, tatu: Need to ensure that not only all Enum implementations get
// there, but also `Enum` -- latter wrt [databind#2605], polymorphic usage
// 判断反序列化的类是否为枚举
if (ClassUtil.isEnumType(type.getRawClass())) { // type.isEnumType()) {
logger.info("\n====>反系列化的类是枚举[{}]");
return factory.createEnumDeserializer(ctxt, type, beanDesc);
}
then. if // 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> deser = _findCustomEnumDeserializer(enumClass, config, beanDesc); last if (deser == null) {
deser = new EnumDeserializer(constructEnumResolver(enumClass,
config, beanDesc.findJsonValueAccessor()),
config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS));
logger.info("\n====>为[{}]调用系统的默认枚举反序列化器[{}]",type,deser);
} if I want to get CustomEnumDeserializer , I have to make deserializers.put(Enum.class, new BaseEnumDeserializer());
jacksonObjectMapperBuilder.deserializersByType(deserializers); I think it's so crazy, I am not sure what's going to happen. I just want to deserializer Enum implemented by SpringBoot how to register deserializers to jackson
/**
* Method for adding deserializer to handle specified type.
*<p>
* WARNING! Type matching only uses type-erased {@code Class} and should NOT
* be used when registering serializers for generic types like
* {@link java.util.Collection} and {@link java.util.Map}.
*/
public <T> SimpleModule addDeserializer(Class<T> type, JsonDeserializer<? extends T> deser)
{
logger.info("\n====>SpringBoot注入起点[{}]===>[{}]",type,deser);
_checkNotNull(type, "type to register deserializer for");
_checkNotNull(deser, "deserializer");
if (_deserializers == null) {
_deserializers = new SimpleDeserializers();
}
_deserializers.addDeserializer(type, deser);
return this;
}
public <T> void addDeserializer(Class<T> forClass, JsonDeserializer<? extends T> deser)
{
ClassKey key = new ClassKey(forClass);
if (_classMappings == null) {
_classMappings = new HashMap<ClassKey,JsonDeserializer<?>>();
}
// =====> 全部添加到类里面了
_classMappings.put(key, deser);
// [Issue#227]: generic Enum deserializer?
if (forClass == Enum.class) {
_hasEnumDeserializer = true;
}
} all class are registered to Last
// 这里应该需要继续查找当前类的父接口,然后再是查找父类
List<JavaType> interfaces = type.getInterfaces();
// 再次查找自定义的序列化器
for (JavaType javaType : interfaces) {
Class<?> rawClass = javaType.getRawClass();
deser = _findCustomEnumDeserializer(rawClass, config, beanDesc);
if (deser != null) {
logger.info("\n====>依据接口[{}]找到了反序列化器[{}]", rawClass, deser);
return deser;
}
} |
@aohanhongzhi I think you are trying to dig too deep into internal handling here; regardless of whether type is
of |
@cowtowncoder Thanks, I'll try to solve it with wrapper
// in spring java config
|
@ZGAOF Good work on overriding parts of |
@cowtowncoder @ZGAOF thank you very much for your help , which will help me solve the problem of customizing enumeration serialization. I've been working on it for a week and I've learned a lot from it. thank you ! |
@aohanhongzhi |
[Chinese]我最近研究枚举的时候遇到一个需求就是希望在SpringBoot框架中将枚举正序列化和反序列化。希望在Controller层接收参数或者返回结果的时候,jackson对我定义的枚举正序列化和反序列化都是可以的。按照面向接口编程的思想,我对所有定义的枚举是先统一实现了一个接口,这个接口有两个方法,code()和description()。也就是枚举在序列化和反序列化的时候,我希望是按照code和description来的,不希望是按照枚举固有的name和ordinal来正反序列化和反序列化。
我阅读了jackson的源码了解到了jackson对枚举的正反序列化之后,我自定义了一个正序列化的枚举序列化器。然后注册到了jackson中,并且也是正常工作的。
但是接下来我在反序列化枚举的时候,发生了一直只能由name或者ordinal来反序列化。我阅读源码后发现我自定义的反序列化器并没有生效,jackson依旧走的是默认的反序列化器,所以不是按照我预期的code或者description来反序列化的。经过阅读源代码之后发现代码中是有查找自定义序列化器的代码。一开始是先找当前类对应的序列化器,但是很明显没有找到,最后只能使用默认的反序列化器。通过源代码发现jackson并没有去查找当前类实现的接口对应的反序列化器。由于缺少这一步,我针对接口注册的反序列化器并没有被使用,因此我在jackson的源码中加入了使用接口去查找自定义序列化器的代码,用来解决这个问题。
这样一来我们就可以做到面向接口编程了。希望采纳。具体使用场景可以参考我的代码。
https://github.com/aohanhongzhi/SpringCloud-multiple-gradle