首页 > 解决方案 > 通过 FasterXML-Jackson 反序列化 Codehaus-Jackson 注释的类实体?

问题描述

我的应用程序有两个模块:一个 API A和一个可视化组件V

一些 API 模型类通过org.codehaus.jackson.annotation(Jackson 1.9.13) 进行注释。在一个逻辑流程中,基于这些模型的对象被 JSON 序列化并交付给V

V目前使用com.fasterxml.jackson.core.ObjectMapper(Jackson 2.9.1) 来反序列化接收到的对象。(V确实有A作为依赖项,因此 Jackson 1.9.13 也可传递地存在于类路径中。)

由于 Jackson 2.9.1 试图将数据反序列化为 Jackson 1.9.13 带注释的类,因此自定义枚举名称映射等某些功能不起作用。例如:

enum Size {

    XL("Extra Large"),
    L("Large"),
    M("Medium"),
    S("Small"),
    XS("Extra Small");

    private String name;

    Size(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @org.codehaus.jackson.annotate.JsonCreator
    public static Size forValue(String value) {
        for (Size size : Size.values()) {
            if (size.getName().equals(value)) {
                return size;
            }
        }
        return null;
    }
}

class Model {
    String name;
    Size size;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSize() {
        return size.getName();
    }

    public void setSize(Size size) {
        this.size = size;
    }
}

V无法反序列化上述Model类中的对象,出现以下错误:

com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `Size` from String "Medium": value not one of declared Enum instance names: [S, XL, M, XS, L]
 at [Source: (String)"{"name":"foo","size":"Medium"}"; line: 1, column: 22] (through reference chain: Model["size"])

这一切都说得通(因为从 1.9.13 到 2.9.1 是向后不兼容的更改),但我有什么选择呢?我是否必须将V的反序列化逻辑降级为 Jackson 1.9.13?或者,是否有任何“适配器”或配置可以“插入”以使 1.9.13 注释在 2.9.1 下工作?

(此时我宁愿不将A升级到 2.9.1,因为A的整体包大小是一个主要问题;切换到A的整体 Jackson 依赖项大小将从 980 KB 增加到 1.51 MB - 不可接受,因为com.fasterxml.jackson.core:jackson-databind完整核心捆绑包(基于A;不包括V)目前的大小远低于 8 MB。)

标签: javajacksonjackson-databindjackson2fasterxml

解决方案


You can customise annotation handling by implementing com.fasterxml.jackson.databind.AnnotationIntrospector class. For example, if we want to allow to use org.codehaus.jackson.annotate.JsonCreator annotation in our model we need to implement handler for it. In other case it will be ignored. Simple implementation could look like below:

class CodehausAnnotationIntrospector extends JacksonAnnotationIntrospector {

    @Override
    public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
        JsonCreator.Mode mode = super.findCreatorAnnotation(config, a);
        if (mode != null) {
            return mode;
        }

        org.codehaus.jackson.annotate.JsonCreator ann = _findAnnotation(a, org.codehaus.jackson.annotate.JsonCreator.class);
        if (ann != null) {
            return JsonCreator.Mode.DEFAULT;
        }

        return null;
    }
}

We just simply enable DEFAULT mode in case org.codehaus.jackson.annotate.JsonCreator annotation is used. Below code shows how to register new introspector:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(new CodehausAnnotationIntrospector());

        Model model = new Model();
        model.setName("Model1");
        model.setSize(Size.XL);

        String json = mapper.writeValueAsString(model);
        System.out.println(json);
        System.out.println(mapper.readValue(json, Model.class));
    }
}

Above code prints:

{"name":"Model1","size":"Extra Large"}
Model{name='Model1', size=XL}

推荐阅读