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

是否有可能使用JAXBparsingxml到一个特定的Java类基于XML的属性?

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

我想有一个包含三angular形和正方形的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 } 

我现在只是把所有的属性放在一个大的“形状”类,并不理想。

我可以得到这个工作,如果形状是正确命名的XML元素,但不幸的是我没有控制我正在检索的XML。

谢谢!

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; } } 

然后使用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"); } } 

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

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

下面是模型类的其余部分:

形状

 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; } } 

广场

 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; } } 

三angular形

 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; } } 

下面是一个演示程序来检查一切正常:

 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); } } 

我希望这有帮助。

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

编辑

在EclipseLink 2.2中,我们使这个configuration更容易,请参阅以下文章以获取更多信息:

注解@XmlElements使您可以指定哪个标记与哪个子类相对应。

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

另请参阅javadoc for @XmlElements

AFAIK,你将不得不写一个XmlAdapter ,它知道如何处理Shape的编组/解组。

不,恐怕这不是一种select,JAXB并不那么灵活。

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

最好的我可以拿出来,对不起。

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

例如,使用以下类定义:

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

用户将被要求创buildJAXBContext作为JAXBContext.newInstance(Dog.class,Cat.class)(动物将被自动拾取,因为狗和猫是指它。)

XmlSeeAlso注释将允许你写:

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