【问题标题】:Java/JAXB: Unmarshall Xml to specific subclass based on an attributeJava/JAXB:根据属性将 Xml 解组为特定子类
【发布时间】:2011-02-28 20:26:41
【问题描述】:

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

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

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

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。

谢谢!

【问题讨论】:

    标签: java inheritance jaxb eclipselink


    【解决方案1】:

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

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

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

    三角形

    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 中,我们使其更易于配置,请查看以下文章了解更多信息:

    【讨论】:

    • 我绝对对这个作为解决方案感兴趣,但我似乎无法让它发挥作用。 Shape 实际上不是我的根元素是否重要?如果我尝试解组集合 JAXB/MOXy 似乎不会打扰使用定制器并抛出一堆关于尝试实例化抽象类的错误。我还缺少另一件作品吗?
    • 我相信您缺少将 EclipseLink MOXy 指定为 JAXB 实现的 jaxb.properties 文件。我已经更新了上面的例子,使其更加完整。
    • 你太棒了!这完美地工作。感谢您不遗余力地帮助我解决这个问题。
    • 类名不等于类型怎么办?如何告诉 JAXB 哪个类型映射到哪个对象?
    • 我发现stackoverflow.com/a/15617571/5987669 描述了如何做同样的事情,但带有注释。我发现它更直观,更易于使用。
    【解决方案2】:

    注解@XmlElements可以让你指定哪个标签对应哪个子类。

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

    另请参阅 @XmlElements 的 javadoc

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • XmlAdapter 用于将单个 String 值转换为特定类型,对于转换复杂类型没有用处。
      • @Skaffman:XmlAdapter 可以轻松用于复杂类型。最常见的用例之一是将 java.util.Map 映射到 XML。
      【解决方案4】:

      不,恐怕这不是一个选项,JAXB 没有那么灵活。

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

      我能想到的最好了,抱歉。

      【讨论】:

      • 我实际上没有想到这一点。当然不理想,但它可能会起作用。我真的希望有某种方式可以放入自定义适配器或其他东西。感谢您的快速回复!
      • @Frothy:JAXB 确实非常有限。有一些替代方案可以提供更大的灵活性,例如 JiBX。
      • 我从未听说过 JiBX,谢谢,今晚我会调查一下。
      • JAXB 不受限制,记住它是一个规范,EclipseLink MOXy 等实现提供扩展以轻松处理此类用例(请参阅我对这个问题的回答)。
      【解决方案5】:

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

      例如,使用以下类定义:

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

      用户需要将 JAXBContext 创建为 JAXBContext.newInstance(Dog.class,Cat.class) (由于 Dog 和 Cat 引用了动物,因此将自动拾取。)

      XmlSeeAlso 注释将允许您编写:

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

      【讨论】:

      • 这是如何解决问题的? (根据元素属性映射不同的类)
      • 我遇到了一个非常相似的问题,这就是我所缺少的,谢谢
      猜你喜欢
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-18
      • 1970-01-01
      • 2011-12-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多