ObjectMapper 无法处理带有遗留枚举(类)的映射对象

Posted

技术标签:

【中文标题】ObjectMapper 无法处理带有遗留枚举(类)的映射对象【英文标题】:ObjectMapper can't handle mapping object with legacy enums (classes) 【发布时间】:2019-06-27 23:24:17 【问题描述】:

我在为一些遗留代码构建 REST 架构时遇到问题。 Jackson ObjectMapper 无法将我的自定义对象映射到旧对象,因为“枚举”实际上是具有静态最终字段的类。

我尝试实现自定义转换器/反序列化器但没有成功

在旧系统中,枚举看起来像这样:

public final class LegacyEnum extends LegacyEnumSuperclass 

    public static final LegacyEnum VALUE = new LegacyEnum("1");

我将这些“枚举”的值作为字符串接收,我将其转换为旧枚举值(自定义反序列化器)并将它们设置在我的自定义类中(我需要它,因为我使用的是 jackson 注释,但我没有访问或修改遗留代码的权限),这部分工作得很好。当我尝试使用

将我的自定义对象映射到旧对象时
objectMapper.convertValue(myCustomObject, LegacyObjectContainingEnums.class); 

我得到一个例外:

Can not construct instance of LegacyEnum: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

LegacyEnum 类有一个私有构造函数,LegacyEnumSuperclass 有一个类似的受保护构造函数,所以我不能访问它们(ObjectMapper 也不能)。我尝试实现一个自定义转换器,它会跳过 ObjectMapper 映射的“创建新对象”部分,并且我还尝试重用我的自定义反序列化器。我遇到了多个问题,但没有成功。

最烦人的部分是,当我使用 ModelMapper 库时,它就像一个魅力(它可能只是在遗留对象中设置一个值,不需要像 ObjectMapper 一样创建新的 LegacyEnum 实例!)但我正在尝试解决该问题没有添加新的依赖项。

有什么想法吗?

【问题讨论】:

我是不是有似曾相识的感觉,还是你昨天问了同样的问题? 不,我做了,但没有得到答案,所以我删除了之前的问题并再次询问。你是个警惕的伙伴,我不得不承认 A) 有趣的问题会留在记忆中 B) 请不要那样做。当您的问题没有引起太多关注时……可能是由于它的编写方式。这个读起来更好。但同样:删除问题并再次提问并不是一种值得赞赏的做法。更好的方法:获得一点声誉,然后在您的问题中添加 赏金。这通常更能吸引人们。最后:可能这里没有解决方案。有不同的框架是有原因的:因为它们只是有不同的优缺点。也许这不能用杰克逊来解决! 感谢您的建议,我刚开始提问,您可能已经知道了,所以我会确保牢记您的建议。祝你有美好的一天:) Lino 在这里是正确的,如果你想去真实的,你应该创建一个真实的minimal reproducible example。不是您的所有代码,而是一个完整的示例(编译并运行)并显示您的错误。 【参考方案1】:

我通过使用 MixIn 和自定义反序列化器解决了这个问题,如下所示:

public abstract class LegacyClassMixIn 
    @JsonDeserialize(using = LegacyEnumDeserializer.class)
    abstract LegacyEnum getLegacyEnum();

反序列化器:

public class LegacyEnumDeserializer extends JsonDeserializer<LegacyEnumSuperclass> implements ContextualDeserializer 

private JavaType valueType;

@Override
public JsonDeserializer createContextual(DeserializationContext context, BeanProperty property) 
    JavaType wrapperType = property.getType();
    LegacyEnumDeserializer deserializer = new LegacyEnumDeserializer();
    deserializer.valueType = wrapperType;

    return deserializer;


@Override
public LegacyEnumSuperclass deserialize(JsonParser parser, DeserializationContext context) throws IOException 
    return LegacyEnumSuperclass.getEnum(valueType.getRawClass(), parser.readValueAs(String.class));

valueType.getRawClass() 返回 LegacyEnum.class,这样我就可以对继承 LegacyEnumSuperclass 类的所有“枚举”使用一个反序列化器。 getEnum 是来自遗留代码的自定义方法。

在 ObjectMapper Spring 配置中注册 MixIn:

@Configuration
public class ObjectMapperConfig 

    public ObjectMapperConfig(ObjectMapper objectMapper) 
        objectMapper.addMixIn(LegacyClass.class, LegacyClassMixIn.class);
    

这样我可以在 Controller 方法中使用 LegacyClass 作为参数。 感谢提供线索。

【讨论】:

以上是关于ObjectMapper 无法处理带有遗留枚举(类)的映射对象的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Jackson ObjectMapper 将 Json 序列化为 Java 对象

无法在 Spring Boot 中将 ProblemHandler 设置为 ObjectMapper

将密码学与 ObjectMapper 一起使用

Jackson的JSON处理-ObjectMapper

带有 Jersey 2.2 和 Jackson 2.1 的自定义 ObjectMapper

带有枚举输入参数的Web Api post方法