2013-05-10 132 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