Java/JAXB:根据属性将 Xml 解组为特定子类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2992234/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:20:41  来源:igfitidea点击:

Java/JAXB: Unmarshall Xml to specific subclass based on an attribute

javainheritancejaxbeclipselink

提问by Frothy

Is it possible to use JAXB to unmarshall xml to a specific Java class based on an attribute of the xml?

是否可以使用 JAXB 根​​据 xml 的属性将 xml 解组为特定的 Java 类?

<shapes>
  <shape type="square" points="4" square-specific-attribute="foo" />
  <shape type="triangle" points="3" triangle-specific-attribute="bar" />
</shapes>

I would like to have a List of Shape objects containing a triangle and a square, each with their own shape-specific attribute. IE:

我想要一个包含三角形和正方形的 Shape 对象列表,每个对象都有自己的特定于形状的属性。IE:

abstract class Shape {
    int points;
    //...etc
}

class Square extends Shape {
    String square-specific-attribute;
    //...etc
}

class Triangle extends Shape {
    String triangle-specific-attribute;
    //...etc
}

I'm currently just putting all attributes in one big "Shape" class and it's less than ideal.

我目前只是将所有属性放在一个大的“形状”类中,这不太理想。

I could get this to work if the shapes were properly named xml elements, but unfortunately I don't have control of the xml I'm retrieving.

如果形状被正确命名为 xml 元素,我可以让它工作,但不幸的是我无法控制我正在检索的 xml。

Thanks!

谢谢!

采纳答案by bdoughan

JAXB is a spec, specific implementations will provide extension points to do things such as this. If you are using EclipseLink JAXB (MOXy)you could modify the Shape class as follows:

JAXB 是一个规范,具体的实现将提供扩展点来做这样的事情。如果您使用EclipseLink JAXB (MOXy),您可以按如下方式修改 Shape 类:

import javax.xml.bind.annotation.XmlAttribute;
import org.eclipse.persistence.oxm.annotations.XmlCustomizer;

@XmlCustomizer(ShapeCustomizer.class)
public abstract class Shape {

    int points;

    @XmlAttribute
    public int getPoints() {
        return points;
    }

    public void setPoints(int points) {
        this.points = points;
    }

}

Then using the MOXy @XMLCustomizer you could access the InheritancePolicy and change the class indicator field from "@xsi:type" to just "type":

然后使用 MOXy @XMLCustomizer 您可以访问 InheritancePolicy 并将类指示符字段从“@xsi:type”更改为“type”:

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;

public class ShapeCustomizer implements DescriptorCustomizer {

    @Override
    public void customize(ClassDescriptor descriptor) throws Exception {
        descriptor.getInheritancePolicy().setClassIndicatorFieldName("@type");
    }
}

You will need to ensure that you have a jaxb.properties file in with you model classes (Shape, Square, etc) with the following entry specifying the EclipseLink MOXy JAXB implementation:

您需要确保在模型类(Shape、Square 等)中有一个 jaxb.properties 文件,并使用以下条目指定 EclipseLink MOXy JAXB 实现:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Below is the rest of the model classes:

以下是其余的模型类:

Shapes

形状

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Shapes {

    private List<Shape> shape = new ArrayList<Shape>();;

    public List<Shape> getShape() {
        return shape;
    }

    public void setShape(List<Shape> shape) {
        this.shape = shape;
    }

}

Square

正方形

import javax.xml.bind.annotation.XmlAttribute;

public class Square extends Shape {
    private String squareSpecificAttribute;

    @XmlAttribute(name="square-specific-attribute")
    public String getSquareSpecificAttribute() {
        return squareSpecificAttribute;
    }

    public void setSquareSpecificAttribute(String s) {
        this.squareSpecificAttribute = s;
    }

}

Triangle

三角形

import javax.xml.bind.annotation.XmlAttribute;

public class Triangle extends Shape {
    private String triangleSpecificAttribute;

    @XmlAttribute(name="triangle-specific-attribute")
    public String getTriangleSpecificAttribute() {
        return triangleSpecificAttribute;
    }

    public void setTriangleSpecificAttribute(String t) {
        this.triangleSpecificAttribute = t;
    }

}

Below is a demo program to check that everything works:

下面是一个演示程序,用于检查一切是否正常:

import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jaxbContext = JAXBContext.newInstance(Shapes.class, Triangle.class, Square.class);

        StringReader xml = new StringReader("<shapes><shape square-specific-attribute='square stuff' type='square'><points>4</points></shape><shape triangle-specific-attribute='triangle stuff' type='triangle'><points>3</points></shape></shapes>");
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        Shapes root = (Shapes) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }
}

I hope this helps.

我希望这有帮助。

For more information on EclipseLink MOXy see:

有关 EclipseLink MOXy 的更多信息,请参阅:

EDIT

编辑

In EclipseLink 2.2 we're making this easier to configure, check out the following article for more information:

在 EclipseLink 2.2 中,我们使它更容易配置,查看以下文章了解更多信息:

回答by skaffman

No, I'm afraid that's not an option, JAXB isn't that flexible.

不,恐怕这不是一种选择,JAXB 不是那么灵活。

The best I can suggest is that you put a method on the Shapeclass which instantiates the "correct" type based on the attribute. The client code would invoke that factory method to obtain it.

我能建议的最好方法是在Shape类上放置一个方法,该方法根据属性实例化“正确”类型。客户端代码将调用该工厂方法来获取它。

Best I can come up with, sorry.

我能想到的最好的,对不起。

回答by Quotidian

AFAIK, you'll have to write an XmlAdapterwhich knows how to handle the marshal/unmarshalling of the Shape.

AFAIK,您必须编写一个XmlAdapter,它知道如何处理 Shape 的编组/解组。

回答by Barmak

The annotation @XmlElementsenables you to specify which tag corresponds with which subclass.

注释@XmlElements使您能够指定哪个标签对应于哪个子类。

@XmlElements({
    @XmlElement(name="square", type=Square.class),
    @XmlElement(name="triangle", type=Triangle.class)
})
public List<Shape> getShape() {
    return shape;
}

Also see javadoc for @XmlElements

另请参阅@XmlElements 的javadoc

回答by Molis

There is @XmlSeeAlso annotation to tell to bind subclasses.

有 @XmlSeeAlso 注释告诉绑定子类。

For example, with the following class definitions:

例如,使用以下类定义:

 class Animal {}
 class Dog extends Animal {}
 class Cat extends Animal {}

The user would be required to create JAXBContext as JAXBContext.newInstance(Dog.class,Cat.class) (Animal will be automatically picked up since Dog and Cat refers to it.)

用户需要将 JAXBContext 创建为 JAXBContext.newInstance(Dog.class,Cat.class) (因为 Dog 和 Cat 引用了 Animal ,所以会自动拾取它。)

XmlSeeAlso annotation would allow you to write:

XmlSeeAlso 注释将允许您编写:

 @XmlSeeAlso({Dog.class,Cat.class})
 class Animal {}
 class Dog extends Animal {}
 class Cat extends Animal {}