2013-05-10 129 views
1

我有一种情况,其中一个Java对象包含需要编组到xml中的通用Payload<T>。因此,考虑以下类:如何使用Java泛型指定JAXB元素的名称?

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> 

我使用@XmlType于具体子类来试图改变​​到small-box但是这也没有工作。如果我删除Payload<P>对象,并且仅拥有泛型类型P的类成员​​,那么paymentContainer标签将消失,但​​仍然存在,并且不使用我指定的small-box名称。

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

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

  1. 我在Payload<T>类使用@XmlTransient并改用一个@XmlAnyElement注解放在setPayload(T payload)方法(虽然我怀疑它并不重要,其所需的setter /吸气对方法注释只要有一个注释)。
  2. 我使用的是Jackson 2的JacksonJaxbXmlProvider,这是一个不完整的JAXB实现,它忽略了用作@XmlAnyElement-annated属性值的元素的@XmlRootElement

更改我的JAXB提供程序以使用Java 6/7内置JAXB提供程序生成我期望的输出。

回答

2

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

  1. 删除@XmlTransient注释,使这些类型可用于编组(我不知道它是如何与这个注解你提到的工作)
  2. 注释setPayloadPayload@XmlAnyElement如下

公共类净荷{ 私人Ť有效载荷;

public T getPayload() { 
    return payload; 
} 

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

}

@XmlAnyElement的Javadoc说

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

这意味着其被传递到setPayload(任何已知的豆型(@XmlRootElement注解))将由JAXB解析为它们相应的类型,在这里,豆是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> 

希望能帮助你。

+0

我已经尝试过您的解决方案,它不会以任何不同的方式编组对象。我正在使用Jackson 2 JAXB库来执行JSON和XML编组。你用什么来获得这个示例代码来产生你的输出? – BrionS 2013-05-11 02:53:00

+1

哦..你可能已经提到过:)我没有使用任何我尝试过使用JAXB标准JDK实现的库。它在JDK 6和7中工作正常 – 2013-05-11 08:55:23

+1

有趣。我将尝试嵌入式JAXB实现和EclipseLink MOXy,因为我知道Jackson并未完全实现JAXB规范。谢谢! – BrionS 2013-05-11 11:26:26