2010-02-12 65 views
19

我有一個JAXB設置,我使用@XmlJavaTypeAdapter將類型爲Person的對象替換爲僅包含此人的UUID的PersonRef類型的對象。這工作非常好。但是,生成的XML在每次使用時都重新聲明相同的名稱空間(xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance")。雖然這通常是好的,但它感覺不對。JAXB:如何避免xmlns的重複名稱空間定義:xsi

如何配置JAXB在文檔的最開始處聲明xmlns:xsi?我可以手動將名稱空間聲明添加到根元素嗎?

這裏是什麼,我想才達到一個例子:

電流:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <!-- SNIP: some more relations --> 
</person> 

通緝:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/> 
    </relation> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/> 
    </relation> 
    <!-- SNIP: some more relations --> 
</person> 

回答

5

您可以用代碼做到這一點:

marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() { 
       @Override 
       public String[] getPreDeclaredNamespaceUris() { 
        return new String[] { 
         XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI 
        }; 
       } 

       @Override 
       public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 
        if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)) 
         return "xsi"; 
        if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) 
         return "xs"; 
        if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI)) 
         return "xmime"; 
        return suggestion; 

       } 
      }); 
+1

我使用jaxb2,這對我並不適用,拋出RuntimeException – arrehman 2012-03-02 19:51:44

0

它是XML,因此你可以使用處理輸出DOM或XSLT來擺脫多個名稱空間引用。

+11

很抱歉,但我寧願拍自己的腳:) – sfussenegger 2010-02-22 13:02:09

+0

但嚴重的是,在這個任務投擲XSLT或DOM似乎有點激烈。最終,這可以歸結爲簡單的請我的美學願望:) – sfussenegger 2010-02-22 13:05:28

+0

JAXB是不可配置的。我認爲這是做到這一點(或放手)的唯一方法。 – 2010-02-22 13:08:10

9

它看起來像一個JAXB customization Namespace mapper issue

當您使用馬歇爾JAXB 1.0,編組器對象,控制編組的處理的JAXB對象的XML文檔,提供所得到的XML文檔中的命名空間聲明。有時的Marshaller產生大量的命名空間聲明看起來多餘的,例如:

<?xml version="1.0"?> 
    <root> 
     <ns1:element xmlns:ns1="urn:foo"> ... </ns1:element> 
     <ns2:element xmlns:ns2="urn:foo"> ... </ns2:element> 
     <ns3:element xmlns:ns3="urn:foo"> ... </ns3:element> 
    </root> 

JAXB 2.0改變了這種行爲。如果使用JAXB 2.0(或更高版本)封送XML文檔,則Marshaller會聲明所有靜態已知名稱空間統一資源標識符(URI),即用作JAXB註釋中元素或屬性名稱的那些URI。例如,當作爲屬性或元素值使用的限定名稱(QName)需要新的名稱空間URI時,或者當文檔對象模型(DOM)被使用時,JAXB還可以在XML文檔的中間聲明其他名稱空間。 )節點在內容樹中需要一個新的名稱空間URI。此行爲可能會生成一個XML文檔,其中包含大量帶有自動生成名稱空間前綴的名稱空間聲明。

問題是自動生成的名稱空間前綴(如ns1,ns2和ns3)不方便用戶使用 - 它們通常不會幫助人們理解編組的XML。

幸運的是,JAXB 2.0(或更高版本)提供了名爲com.sun.xml.bind.marshaller.NamespacePrefixMapper的服務提供者接口(SPI),您可以使用它來指定用於編組的更有用的名稱空間前綴。

當JAXBSample程序第一次收發XML文檔時,它會在不使用NamespacePrefixMapper類的情況下執行此操作。因此,Marshaller會自動生成一個名稱空間前綴,在本例中爲ns2。的結構避免了命名空間重複的

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
    <ns2:JustAnElement xmlns:ns2="a"> 
     <foo>true</foo> 
    </ns2:JustAnElement> 

實施例:

JAXBSample程序所做的第二編組使用NamespacePrefixMapper類,如下所示:

NamespacePrefixMapper m = new PreferredMapper(); 
       marshal(jc, e, m); 

    public static class PreferredMapper extends NamespacePrefixMapper { 
      @Override 
      public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 
       return "mappedNamespace" + namespaceUri; 
      } 
     } 

getPreferredPrefix()中的PreferredMapper類中的方法將返回首選前綴,在此情況下,mappedNamespacea將在編組XML的根元素處聲明。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
    <mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a"> 
     <foo>true</foo> 
    </mappedNamespacea:JustAnElement> 
+0

另請參閱http://khylo.blogspot.com/2008/08/jaxb-multiple-namespace-definitions.html關於非直接相關的問題。 – VonC 2010-02-22 13:16:41

+0

感謝您的回答。默認的NamespacePrefixMapper已經將「http://www.w3.org/2001/XMLSchema-instance」映射爲「xsi」(請參閱​​http://j.mp/dkCcgC)。所以我認爲沒有辦法使用NamespacePrefixMapper來更早地聲明命名空間。 NamespaceContext看起來很有希望。儘管如此,我仍然試圖弄清楚如何實現這一目標。 (無論如何+1爲你的努力) – sfussenegger 2010-02-23 09:33:02

+0

如果你感興趣:3個月後,事實證明你的答案並不太遠:正確的類,錯誤的方法(請參閱Dany的答案)。我無法相信在檢查您的建議時我沒有注意到getPreDeclaredNamespaceUris()方法。 – sfussenegger 2010-05-31 12:51:19

16

漂亮,但你可以添加一個空的schemaLocation根元素:

marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, ""); 
+0

的確不是*那*漂亮:)但至少它接近於正確的答案。 – sfussenegger 2010-03-01 09:07:40

+0

我覺得它很華麗!我不得不繼續使用較舊的JAXB 2.x繼續使用Java 6運行時。隨着您的更改,我只在根元素上獲得架構聲明,但不在任何子元素上。現在根元素看起來像這樣:'' – 2016-10-04 12:12:20

3

,如果你'重新使用Maven,然後將其添加到您的pom中:

<dependency> 
      <groupId>com.sun.xml.bind</groupId> 
      <artifactId>jaxb-impl</artifactId> 
      <version>2.2.2</version> 
      <type>jar</type> 
      <scope>compile</scope> 
     </dependency> 

如果您配置上述示例中定義的註釋,則無需使用PreferredMapper。雖然我有一個package-info.jave文件如下配置:

@javax.xml.bind.annotation.XmlSchema(
     namespace = "mylovelynamespace1", 
     xmlns = { 
        @javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"), 
        @javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2") 
       }, 
       elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
package com.mylovelycompanyname.package; 
2

這是我在網上找到的最佳答案。

xsi:type聲明最有可能被創建,因爲JAXBElement的聲明類型與值的類型不匹配。

如果ObjectFactory有一個正確的JAXBElement的創建方法,您應該使用它,因爲它應該正確填充QName和類型信息;否則,我會嘗試設置JAXBElementString.class(假定這是commentTest的類型)的聲明類型(第二構造函數arg)而不是CommentType.Comment

來源: http://www.java.net/forum/topic/glassfish/metro-and-jaxb/how-do-i-remove-namespace-declarations-child-elements

業主: cbrettin

1

您可以讓命名空間只能寫入一次。您將需要一個XMLStreamWriter的代理類和一個package-info.java。然後你會在你的代碼做:

StringWriter stringWriter = new StringWriter(); 
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory 
                   .newInstance().createXMLStreamWriter(stringWriter)); 
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class); 
Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); 
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 
jaxbMarshaller.marshal(books, writer); 
System.out.println(stringWriter.toString()); 

Proxy類(重要的方法是 「和writeNamespace」):

  class WrapperXMLStreamWriter implements XMLStreamWriter { 

        private final XMLStreamWriter writer; 

        public WrapperXMLStreamWriter(XMLStreamWriter writer) { 
         this.writer = writer; 
        } 

        //keeps track of what namespaces were used so that not to 
        //write them more than once 
        private List<String> namespaces = new ArrayList<String>(); 

        public void init(){ 
         namespaces.clear(); 
        } 

        public void writeStartElement(String localName) throws XMLStreamException { 
         init(); 
         writer.writeStartElement(localName); 

        } 

        public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException { 
         init(); 
         writer.writeStartElement(namespaceURI, localName); 
        } 

        public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { 
         init(); 
         writer.writeStartElement(prefix, localName, namespaceURI); 
        } 

        public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException { 
         if(namespaces.contains(namespaceURI)){ 
          return; 
         } 
         namespaces.add(namespaceURI); 
         writer.writeNamespace(prefix, namespaceURI); 
        } 

    // .. other delegation method, always the same pattern: writer.method() ... 

} 

包信息。Java的:

@XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED , 
     xmlns = { 
     @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")}) 
package your.package; 

import javax.xml.bind.annotation.XmlNs; 
import javax.xml.bind.annotation.XmlNsForm; 
import javax.xml.bind.annotation.XmlSchema; 
0

通過這樣添加您nsPrefix映射:

marshaller.setNamespaceMapping("myns","urn:foo");

相關問題