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