JAXB继承冲突 - 在子类上重新注释

时间:2012-07-28 04:04:23

标签: java xml jaxb

我目前在我的项目中有这个环境:

public abstract class Foo {

   private List<Thing> things;

   public List<Thing> getThings() { return this.things; }
}

public abstract class Bar extends Foo {


   @XmlElements({@XmlElement(name = "first", type = First.class)})
   public List<Thing> getThings() { return super.getThings(); }

}

public class Bobar extends Bar {

   @XmlElements({@XmlElement(name = "second", type = Second.class)})
   public List<Thing> getThings() { return super.getThings(); }

}

对于以下XML文档

<bobar>
   <first>blablabla</first>
   <second>blublublu</second>
</bobar>

当我这样做时

context = JAXBContext.newInstance("the.package.structure");
unmarshaller = context.createUnmarshaller();
Bar object = (Bar) unmarshaller.unmarshal("path-to-xml-document");

object在集合中只有一个元素,而不是2. First元素完全丢失,当我尝试object.getThings()时,它的大小为1且是唯一的集合中的对象是Second的实例。有人可以帮助我如何在集合中获取两个对象?如果那是不可能的,我怎样才能实现与此相似的东西呢?

我这样做的原因是(在我的项目逻辑中)每个Bobar的东西集合在其集合中都有First,但不是每个Bar都有{Second 1}}在其集合中,Foo是一个通用类。

编辑:

当我更改XML文档中的顺序时,输出是不同的。

<bobar>
   <second>blablabla</second>
   <first>blublublu</first>
</bobar>

在这种情况下,我只在集合中获得First的实例,并且Second丢失。更改方案,我得到了有趣的结果:

public abstract class Foo {

   private List<Thing> things;

   public List<Thing> getThings() { return this.things; }
}

public abstract class Bar extends Foo {


   @XmlElements({@XmlElement(name = "first", type = First.class), @XmlElement(name = "third, type = Third.class)})
   public List<Thing> getThings() { return super.getThings(); }

}

public class Bobar extends Bar {

   @XmlElements({@XmlElement(name = "second", type = Second.class)})
   public List<Thing> getThings() { return super.getThings(); }

}

如果我这样做

<bobar>
   <third>bliblibli</third>
   <second>blablabla</second>
   <first>blublublu</first>
</bobar>

理论上,我认为这不应该针对由此生成的XML Schema进行验证,因为此处的顺序不正确。但除此之外,在这种情况下,我得到SecondFirstThird丢失了。

2 个答案:

答案 0 :(得分:4)

无法在超类型上注释属性,并且可以逐步添加子映射。以下是一种可以支持您所使用的所有用例的方法。需要注意的一件事是,对象层次结构中的所有级别都支持同一组元素。您需要使用外部验证方法来限制所需的值。

如果Thing是一个类而不是一个界面,并且FirstSecond延伸Thing,那么您可能有兴趣使用@XmlElementRef代替{{ 1}}(参见:http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html)。它将以一些验证为代价提供更多灵活性(难以限制有效值集)。

<强>酒吧

我们将使用@XmlElements注释Bar,以便JAXB实现不会处理它。

@XmlTransient

<强> Bobar则

package forum11698160; import java.util.List; import javax.xml.bind.annotation.XmlTransient; @XmlTransient public abstract class Bar extends Foo { public List<Thing> getThings() { return super.getThings(); } } 对应于XML模式中替换组的概念。与属性匹配的值将基于@XmlElementRef声明。

@XmlRootElement

<强>事

由于JAXB实现不能使用反射来查找类型的所有子类,因此我们可以使用package forum11698160; import java.util.List; import javax.xml.bind.annotation.*; @XmlRootElement public class Bobar extends Bar { @XmlElementRef public List<Thing> getThings() { return super.getThings(); } } 注释来帮助解决问题。如果您不使用此注释,则需要在引导@XmlSeeAlso时包含所有子类型。

JAXBContext

<强>第一

我们需要使用package forum11698160; import javax.xml.bind.annotation.XmlSeeAlso; @XmlSeeAlso({First.class, Second.class}) public class Thing { } 注释First

@XmlRootElement

<强>第二

package forum11698160; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class First extends Thing { } 还需要使用Second注释:

@XmlRootElement

<强>演示

package forum11698160;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Second extends Thing {

}

<强> input.xml中/输出

package forum11698160;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Bobar.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum11698160/input.xml");
        Bobar bobar =  (Bobar) unmarshaller.unmarshal(xml);

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

}

其他文件

以下是运行此示例所需的其他文件:

<强>富

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bobar>
    <first/>
    <second/>
</bobar>

答案 1 :(得分:2)

以下是利用@XmlTransient

来映射此用例的方法

<强> Bobar则

您无法在子类中扩展映射,因此您需要确保在执行@XmlElements映射时执行所有操作(请参阅:http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html)。

package forum11698160;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Bobar extends Bar {

    @XmlElements({ 
        @XmlElement(name = "first", type = First.class),
        @XmlElement(name = "second", type = Second.class)
    })
    public List<Thing> getThings() {
        return super.getThings();
    }

}

<强>酒吧

您也无法覆盖继承的属性。因此,我们需要在类型级别使用@XmlTransient来表示我们不希望我们的JAXB实现处理超类型(请参阅:http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html)。

package forum11698160;

import java.util.List;

import javax.xml.bind.annotation.*;

@XmlTransient
public abstract class Bar extends Foo {

    public List<Thing> getThings() {
        return super.getThings();
    }

}

<强>演示

以下演示代码可用于演示一切正常:

package forum11698160;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Bobar.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum11698160/input.xml");
        Bobar bobar =  (Bobar) unmarshaller.unmarshal(xml);

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

}

<强> input.xml中/输出

<?xml version="1.0" encoding="UTF-8"?>
<bobar>
   <first/>
   <second/>
</bobar>

其他文件

以下是运行此示例所需的其他文件:

<强>富

package forum11698160;

import java.util.*;

public abstract class Foo {

    private List<Thing> things = new ArrayList<Thing>();

    public List<Thing> getThings() {
        return this.things;
    }

}

<强>事

package forum11698160;

public interface Thing {

}

<强>第一

package forum11698160;

public class First implements Thing {

}

<强>第二

package forum11698160;

public class Second implements Thing {

}