当前位置: 首页 > 知识库问答 >
问题:

Jackson根据类型反序列化

张鹏鹍
2023-03-14

假设我有以下格式的JSON:

{
    "type" : "Foo"
    "data" : {
        "object" : {
            "id" : "1"
            "fizz" : "bizz"
            ...
        },
        "metadata" : {
            ...
        },
        "owner" : {
            "name" : "John"
            ...
        }
    }
}

我试图避免自定义反序列化程序,并试图将上述JSON(称为Wrapper.java)反序列化为JavaPOJO。“type”字段指示“object”反序列化,即type=foo表示使用foo.java反序列化“object”字段。(如果type=Bar,则使用Bar.java反序列化对象字段)。Metadata/owner将始终以相同的方式对每个元数据使用简单的带Jackson注释的Java类进行反序列化。有没有一种方法可以通过注释来实现这一点?如果不是,如何使用自定义反序列化程序来实现?

共有3个答案

上官思博
2023-03-14

所有这些都可以通过注释来完成。

创建一个抽象超类,其中包含“metadata”和“owner”等公共字段及其getter/setter。这个类需要用@JsonTypeInfo注释。它应该是这样的:

@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "type")

通过参数属性="type",您可以指定类标识符将在JSON文档中的字段类型下序列化。

类标识符的值可以用use.Id指定。CLASS使用完全限定Java类名。也可以使用Id。MINIMAL_CLASS这是一个缩写Java类名。要拥有自己的标识符,请使用Id。名称。在这种情况下,您需要声明子类型:

@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Foo.class, name = "Foo"),
    @JsonSubTypes.Type(value = Bar.class, name = "Bar")
})

通过扩展抽象超类来实现类Foo和Bar。

Jackson的ObjectMapper将使用JSON文档的附加字段“type”进行序列化和反序列化。例如。将JSON字符串反序列化为超类引用时,它将属于适当的子类:

ObjectMapper om = new ObjectMapper();
AbstractBase x = om.readValue(json, AbstractBase.class);
// x will be instanceof Foo or Bar


完整的代码示例(我使用公共字段作为不需要编写getters/setters的快捷方式):

package test;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonSubTypes;

@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Foo.class, name = "Foo"),
    @JsonSubTypes.Type(value = Bar.class, name = "Bar")
})
public abstract class AbstractBase {

    public MetaData metaData;
    public Owner owner;
    @Override
    public String toString() {
        return "metaData=" + metaData + "; owner=" + owner;
    }

    public static void main(String[] args) throws IOException {

        // Common fields
        Owner owner = new Owner();
        owner.name = "Richard";
        MetaData metaData = new MetaData();
        metaData.data = "Some data";

        // Foo
        Foo foo = new Foo();
        foo.owner = owner;
        foo.metaData = metaData;
        CustomObject customObject = new CustomObject();
        customObject.id = 20l;
        customObject.fizz = "Example";
        Data data = new Data();
        data.object = customObject;
        foo.data = data;
        System.out.println("Foo: " + foo);

        // Bar
        Bar bar = new Bar();
        bar.owner = owner;
        bar.metaData = metaData;
        bar.data = "A String in Bar";

        ObjectMapper om = new ObjectMapper();

        // Test Foo:
        String foojson = om.writeValueAsString(foo);
        System.out.println(foojson);
        AbstractBase fooDeserialised = om.readValue(foojson, AbstractBase.class);
        System.out.println(fooDeserialised);

        // Test Bar:
        String barjson = om.writeValueAsString(bar);
        System.out.println(barjson);
        AbstractBase barDeserialised = om.readValue(barjson, AbstractBase.class);
        System.out.println(barDeserialised);

    }

}

class Foo extends AbstractBase {
    public Data data;
    @Override
    public String toString() {
        return "Foo[" + super.toString() + "; data=" + data + ']';
    }
}

class Bar extends AbstractBase {
    public String data;
    public String toString() {
        return "Bar[" + super.toString() + "; data=" + data + ']';
    }
}


class Data {
    public CustomObject object;
    @Override
    public String toString() {
        return "Data[object=" + object + ']';
    }
}

class CustomObject {
    public long id;
    public String fizz;
    @Override
    public String toString() {
        return "CustomObject[id=" + id + "; fizz=" + fizz + ']';
    }
}

class MetaData {
    public String data;
    @Override
    public String toString() {
        return "MetaData[data=" + data + ']';
    }
}

class Owner {
    public String name;
    @Override
    public String toString() {
        return "Owner[name=" + name + ']';
    }
}
方通
2023-03-14

您可以使用检查类型属性的自定义反序列化程序将对象属性解析为最合适的类。

首先定义一个将由FooBar类实现的接口:

public interface Model {

}
public class Foo implements Model {

    // Fields, getters and setters
}
public class Bar implements Model {

    // Fields, getters and setters
}

然后定义包装器数据类:

public class Wrapper {

    private String type;

    private Data data;

    // Getters and setters
}
public class Data {

    @JsonDeserialize(using = ModelDeserializer.class)
    private Model object;

    private Metadata metadata;

    private Owner owner;

    // Getters and setters
}

对象字段用@JsonDeserialize注释,指示将用于对象属性的反序列化程序。

反序列化器定义如下:

public class ModelDeserializer extends JsonDeserializer<Model> {

    @Override
    public Model deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonMappingException {

        // Get reference to ObjectCodec
        ObjectCodec codec = jp.getCodec();

        // Parse "object" node into Jackson's tree model
        JsonNode node = codec.readTree(jp);

        // Get value of the "type" property
        String type = ((Wrapper) jp.getParsingContext().getParent()
            .getCurrentValue()).getType();

        // Check the "type" property and map "object" to the suitable class
        switch (type) {

            case "Foo":
                return codec.treeToValue(node, Foo.class);

            case "Bar":
                return codec.treeToValue(node, Bar.class);

            default:
                throw new JsonMappingException(jp, 
                    "Invalid value for the \"type\" property");
        }
    }
}

JSON文档可以按如下方式反序列化:

ObjectMapper mapper = new ObjectMapper();
Wrapper wrapper = mapper.readValue(json, Wrapper.class);  

对于这个自定义反序列化器,考虑只注释的方法。

马宜民
2023-03-14

除自定义反序列化器方法外,对于仅注释的解决方案,您可以使用以下方法(类似于Spunc的答案中描述的解决方案,但使用type作为外部属性):

public abstract class AbstractData {

    private Owner owner;

    private Metadata metadata;

    // Getters and setters
}
public static final class FooData extends AbstractData {

    private Foo object;

    // Getters and setters
}
public static final class BarData extends AbstractData {

    private Bar object;

    // Getters and setters
}
public class Wrapper {

    private String type;

    @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
    @JsonSubTypes(value = { 
            @JsonSubTypes.Type(value = FooData.class, name = "Foo"),
            @JsonSubTypes.Type(value = BarData.class, name = "Bar") 
    })
    private AbstractData data;

    // Getters and setters
}

在这种方法中,@JsonTypeInfo被设置为使用类型作为外部属性,以确定映射数据属性的正确类。

JSON文档可以按如下方式反序列化:

ObjectMapper mapper = new ObjectMapper();
Wrapper wrapper = mapper.readValue(json, Wrapper.class);  
 类似资料:
  • 问题内容: 可以说我有以下格式的JSON: 我试图避免自定义反序列化器,并尝试将上述JSON(称为Wrapper.java)反序列化为Java POJO。“类型”字段指示“对象”反序列化,即。type = foo表示使用Foo.java反序列化“ object”字段。(如果type = Bar,则使用Bar.java反序列化对象字段)。元数据/所有者将始终使用相同的反序列化方式,每个方法都使用一个

  • 问题内容: 我正在尝试制作一个使用Jackson来反序列化POJO的类。 看起来像这样… 我对此实施有2个问题。 首先是我将类类型传递给方法,以便对象映射器知道应反序列化的类型。有使用泛型的更好方法吗? 同样在get方法中,我将一个从objectMapper返回的对象强制转换为T。这看起来特别讨厌,因为我必须在此处强制转换T,然后还必须从调用它的方法中强制转换对象类型。 我在该项目中使用了Robo

  • 问题内容: 我有一个json字符串,应该将其反序列化为以下类 我该怎么做?这是通常的方式 但是我怎么提到T代表什么呢? 问题答案: 你需要为使用的每种通用类型创建一个对象,并将其用于反序列化。例如

  • 问题内容: 如果我有这样的类结构: 还有另一种反序列化的方法吗?在父类上使用此注释: 我不想强迫我的API的客户包括反序列化一个子类。 杰克逊不是使用,而是提供了一种方法来注释子类并通过唯一属性将其与其他子类区分开来?在上面的示例中,这类似于“如果JSON对象将其反序列化为,如果它将其反序列化为”。 问题答案: 感觉像是应该使用的东西,但是我在文档中进行了选择,可以提供的任何属性似乎都不符合您所描

  • 我看过jackson反序列化@JsonTypeInfo的一个例子,那就是: 我试过了,效果很好。现在的问题是,在示例类中,Cat和Dog是从Animal中引用的,我想避免这种情况。有没有一种方法可以将类型绑定从类动物中移除,并且仍然进行反序列化工作?谢谢