如何使用Java泛型指定JAXB元素的名称?

时间:2013-05-10 17:38:33

标签: java generics jaxb annotations abstract-class

我遇到的情况是Java对象包含需要编组到xml中的通用Payload<T>。所以给出以下类:

AbstractBox

@XmlTransient
public abstract class AbstractBox {
    String type = this.getClass().getSimpleName();
    String name = this.getClass().getCanonicalName();

    // setters/getters snipped

    public abstract String saySomething();
}

在smallBox

@XmlRootElement(name="small-box")
@XmlType(name="small-box")
public class SmallBox extends AbstractBox {

    @XmlElement
    public String getMsg() {
        return saySomething();
    }
    public void setMsg(String msg) {
        // do nothing
    }

    @Override
    public String saySomething() {
        return "I'm a small box";
    }
}

有效载荷

@XmlTransient
public class Payload<T> {
    private T payload;

    public T getPayload() {
        return payload;
    }

    public void setPayload(T payload) {
        this.payload = payload;
    }
}

和一些像这样的代码:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class JaxbAnnotationsTest<P>
{
    String name;
    int age;
    int id;
    Payload<P> payload;

    // setters and getters snipped

    public static void main(String[] args) {
        JaxbAnnotationsTest<AbstractBox> example = new JaxbAnnotationsTest<AbstractBox>();
        example.setName("Brion");
        example.setId(100);
        example.setAge(34);
        Payload<AbstractBox> object = new Payload<AbstractBox>();
        object.setPayload(new SmallBox());
        example.setPayloadContainer(object);

        try {
            XmlMapper xmapper = new XmlMapper();
            xmapper.writeValue(System.out, example);
        } catch (Exception ex) {
            System.err.println("Damn..." + ex.getMessage());
        }
    }

我希望看到:

<JaxbAnnotationsTest>
  <name>Brion</name>
  <age>34</age>
  <id>100</id>
  <payloadContainer>
    <small-box>
      <type>SmallBox</type>
      <name>sandbox.xml.jaxb.annotations.SmallBox</name>
      <msg>I'm a small box</msg>
    </small-box>
  </payloadContainer>
</JaxbAnnotationsTest>

但我得到了:

<JaxbAnnotationsTest>
  <name>Brion</name>
  <age>34</age>
  <id>100</id>
  <payloadContainer>
    <payload>
      <type>SmallBox</type>
      <name>sandbox.xml.jaxb.annotations.SmallBox</name>
      <msg>I'm a small box</msg>
    </payload>
  </payloadContainer>
</JaxbAnnotationsTest>

我已尝试在具体子类上使用@XmlTypepayload更改为small-box,但这也无效。如果我删除了Payload<P>对象并且只是拥有通用类型P的类成员payload,则paymentContainer标记会消失,但payload仍然存在且不会使用{{ 1}}我已指定名称。

有没有办法强制JAXB(任何实现)将标记设置为子类中指定的名称而不是泛型类型属性?

更新 选定的答案提供了解决方案,但我也想跟进问题。我的问题是双重的:

  1. 我在small-box课程上使用了@XmlTransient,而是需要在Payload<T>方法上使用@XmlAnyElement注释(尽管我怀疑它并不重要只要只有一个具有注释,就会注释setter / getter对的哪个方法。
  2. 我使用的是杰克逊2 setPayload(T payload),这是一个不完整的JAXB实现,它忽略了用作JacksonJaxbXmlProvider - 注释属性值的@XmlRootElement元素。
  3. 更改我的JAXB提供程序以使用Java 6/7内置JAXB提供程序生成了我期望的输出。

1 个答案:

答案 0 :(得分:2)

您的Payload类期望任何bean类型作为属性,因此JAXB不知道如何编组该特定对象(在本例中为SmallBox)。由于您需要在Payload中保留通用属性,因此解决方案应该是

  1. 删除@XmlTransient注释以使这些类型可用于编组(我想知道它如何与您提到的注释一起使用)
  2. 使用setPayloadPayload课程中注释@XmlAnyElement,如下所示
  3. 公共课Payload {     私人有效载荷;

    public T getPayload() {
        return payload;
    }
    
    @XmlAnyElement
    public void setPayload(T payload) {
        this.payload = payload;
    }
    

    }

    @XmlAnyElement javadoc说

      

    将JavaBean属性映射到XML信息集表示和/或JAXB   元件。

    这意味着传递到@XmlRootElement()的任何已知bean类型(注释为setPayload)将由JAXB解析为其对应的类型,此处bean为SmallBox,否则为默认元素类型(我认为它应该是org.w3c.dom.Element的默认实现)。在此更改之后,它将JaxbAnnotationsTest很好地编组到跟随xml

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <jaxbAnnotationsTest>
        <age>34</age>
        <id>100</id>
        <name>Brion</name>
        <payloadContainer>
            <small-box>
                <name>org.mycode.SmallBox</name>
                <type>SmallBox</type>
                <msg>I'm a small box</msg>
            </small-box>
        </payloadContainer>
    </jaxbAnnotationsTest>
    

    希望能帮助你。