2011-02-02 206 views
8

jaxb如何確定彙編對象的名稱空間前綴聲明列表?我用xjc編譯了ebics的java類(ebics schema)。當我爲ebicsRequest創建一個實例,它看起來像這樣:控制JAXB中的名稱空間前綴


<?xml version="1.0" encoding="UTF-16"?> 
<ns2:ebicsRequest xmlns:ns2="http://www.ebics.org/H003" Revision="1" Version="H003" xmlns="http://www.ebics.org/H003" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ns4="http://www.ebics.org/S001" xmlns:ns5="http://www.ebics.org/H000"> 
    <ns2:header authenticate="true"> 
     <ns2:static> 
      <ns2:HostID>SIZBN001</ns2:HostID> 
      <ns2:Nonce>A5488F43223063171CA0FA59ADC635F0</ns2:Nonce> 
      <ns2:Timestamp>2009-08-04T08:41:56.967Z</ns2:Timestamp> 
      <ns2:PartnerID>EBICS</ns2:PartnerID> 
      <ns2:UserID>EBIX</ns2:UserID> 
      <ns2:Product Language="de">EBICS-Kernel V2.0.4, SIZ/PPI</ns2:Product> 
      <ns2:OrderDetails> 
       <ns2:OrderType>FTB</ns2:OrderType> 
       <ns2:OrderID>A037</ns2:OrderID> 
       <ns2:OrderAttribute>OZHNN</ns2:OrderAttribute> 
       <ns2:StandardOrderParams/> 
      </ns2:OrderDetails> 
      <ns2:BankPubKeyDigests> 
       <ns2:Authentication Algorithm="RSA" Version="X002">...</ns2:Authentication> 
       <ns2:Encryption Algorithm="RSA" Version="E002">...</ns2:Encryption> 
      </ns2:BankPubKeyDigests> 
      <ns2:SecurityMedium>0000</ns2:SecurityMedium> 
      <ns2:NumSegments>1</ns2:NumSegments> 
     </ns2:static> 
     <ns2:mutable> 
      <ns2:TransactionPhase>Initialisation</ns2:TransactionPhase> 
     </ns2:mutable> 
    </ns2:header> 
    <ns2:AuthSignature> 
     <ds:SignedInfo> 
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> 
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> 
      <ds:Reference URI="#xpointer(//*[@authenticate='true'])"> 
       <ds:Transforms> 
        <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> 
       </ds:Transforms> 
       <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> 
       <ds:DigestValue>CSbjPbiNcFqSl6lCI1weK5x1nMeCH5bTQq5pedq5uI0=</ds:DigestValue> 
      </ds:Reference> 
     </ds:SignedInfo> 
     <ds:SignatureValue>...</ds:SignatureValue> 
    </ns2:AuthSignature> 
    <ns2:body> 
     <ns2:DataTransfer> 
      <ns2:DataEncryptionInfo authenticate="true"> 
       <ns2:EncryptionPubKeyDigest Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" Version="E002">dFAYe281vj9NB7w+VoWIdfHnjY9hNbZLbHsDOu76QAE=</ns2:EncryptionPubKeyDigest> 
       <ns2:TransactionKey>...</ns2:TransactionKey> 
      </ns2:DataEncryptionInfo> 
      <ns2:SignatureData authenticate="true">...</ns2:SignatureData> 
     </ns2:DataTransfer> 
    </ns2:body> 
</ns2:ebicsRequest> 

我已經使用了自定義NamespacePrefixMapper宣佈爲DS和XSI的默認命名空間和前綴。對於名稱空間ds,它工作正常。但是對於默認名稱空間,它不會。它被宣佈一次爲ns2,一次爲「」,後者來自我的自定義NamespacePrefixMapper.getPreDeclaredNamespaceUris。我在這堂課中玩過很多。此外,我嘗試使用package-info.java,但我無法讓jaxb使用"http://www.ebics.org/H003"作爲默認命名空間。我也不明白的是ns4和ns5的外觀,它們完全不屬於xml文檔。

我NamespacePrefixMapper類是什麼樣子


public class NamespacePrefixMapperImpl extends NamespacePrefixMapper implements NamespaceContext { 
    private static final String[] EMPTY_STRING = new String[0]; 

    private Map prefixToUri = null; 
    private Map uriToPrefix = null; 

    private void init(){ 
    prefixToUri = new HashMap(); 

    prefixToUri.put("", "http://www.ebics.org/H003"); 
    prefixToUri.put("ds", "http://www.w3.org/2000/09/xmldsig#"); 
    prefixToUri.put("xsi", "http://www.w3.org/2001/XMLSchema-instance"); 
    prefixToUri.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI ); 
    prefixToUri.put(XMLConstants.XMLNS_ATTRIBUTE , XMLConstants.XMLNS_ATTRIBUTE_NS_URI); 

    uriToPrefix = new HashMap(); 
    for(String prefix : prefixToUri.keySet()){ 
     uriToPrefix.put(prefixToUri.get(prefix), prefix); 
    } 
    } 

    @Override 
    public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 
     if (uriToPrefix == null) 
     init(); 

     if (uriToPrefix.containsKey(namespaceUri)){ 
      return uriToPrefix.get(namespaceUri); 
     } 

     return suggestion; 
    } 

    @Override 
    public String[] getContextualNamespaceDecls() { 
    // TODO Auto-generated method stub 
    return EMPTY_STRING; 
    } 

    @Override 
    public String[] getPreDeclaredNamespaceUris() { 
    // TODO Auto-generated method stub 
    return EMPTY_STRING; 

    } 

    @Override 
    public String[] getPreDeclaredNamespaceUris2() { 
    return new String [] {"", prefixToUri.get("")}; 

    } 

    public String getNamespaceURI(String prefix) { 
    if (prefixToUri == null) 
      init(); 

    if (prefixToUri.containsKey(prefix)) { 
     return prefixToUri.get(prefix); 
    } else { 
     return XMLConstants.NULL_NS_URI; 
    } 
    } 

    public String getPrefix(String namespaceURI) { 
    if (uriToPrefix == null) 
      init(); 

     if (uriToPrefix.containsKey(namespaceURI)){ 
     return uriToPrefix.get(namespaceURI); 
    } else { 
     return null; 
    } 
    } 

    public Iterator getPrefixes(String namespaceURI) { 
    if (uriToPrefix == null) 
      init(); 

    List prefixes = new LinkedList(); 

    if (uriToPrefix.containsKey(namespaceURI)){ 
     prefixes.add(uriToPrefix.get(namespaceURI)); 
    } 
    return prefixes.iterator(); 
    } 


} 

我使用


Manifest-Version: 1.0 
Ant-Version: Apache Ant 1.6.5 
Created-By: 1.5.0-b64 (Sun Microsystems Inc.) 
Specification-Title: Java Architecture for XML Binding 
Specification-Version: 2.0 
Specification-Vendor: Sun Microsystems, Inc. 
Implementation-Title: JAXB Reference Implementation 
Implementation-Version: 2.0.2 
Implementation-Vendor: Sun Microsystems, Inc. 
Implementation-Vendor-Id: com.sun 
Extension-Name: com.sun.xml.bind 
Build-Id: b01 
Class-Path: jaxb-api.jar activation.jar jsr173_1.0_api.jar jaxb1-impl. 
jar 

Name: com.sun.xml.bind.v2.runtime 
Implementation-Version: 2.0.2-b01-fcs 
+1

請參閱http://stackoverflow.com/questions/1982977/is-it-possible-to-customize-the-namespace-prefix-that-jaxb-uses-when-marshalling – skaffman 2011-02-02 10:47:38

+0

您可否附上您的NamespacePrefixMapper實施? – sfussenegger 2011-02-02 11:00:20

+0

Wilberforce 2011-02-02 11:02:09

回答

8

JAXB總是添加了由JAXBContext中已知出於性能考慮XML文檔的根元素的所有命名空間。請參閱Kohsuke的評論JAXB-103瞭解更多信息。

我找到應對這個問題的唯一辦法,是使用下面的輔助類遍歷文檔自己已經創建後JAXB和刪除所有未使用的命名空間:

public class RemoveUnusedNamespaces { 

    private static final String XML_NAMESPACE_SCHEMA_INSTANCE = "http://www.w3.org/2001/XMLSchema-instance"; 

    private static final String XML_NAMESPACE_NAMESPACE = "http://www.w3.org/2000/xmlns/"; 

    private interface ElementVisitor { 

     void visit(Element element); 

    } 

    public void process(Document document) { 
     final Set<String> namespaces = new HashSet<String>(); 

     Element element = document.getDocumentElement(); 
     traverse(element, new ElementVisitor() { 

      public void visit(Element element) { 
       String namespace = element.getNamespaceURI(); 
       if (namespace == null) 
        namespace = ""; 
       namespaces.add(namespace); 
       NamedNodeMap attributes = element.getAttributes(); 
       for (int i = 0; i < attributes.getLength(); i++) { 
        Node node = attributes.item(i); 
        if (XML_NAMESPACE_NAMESPACE.equals(node.getNamespaceURI())) 
         continue; 
        String prefix; 
        if (XML_NAMESPACE_SCHEMA_INSTANCE.equals(node.getNamespaceURI())) { 
         if ("type".equals(node.getLocalName())) { 
          String value = node.getNodeValue(); 
          if (value.contains(":")) 
           prefix = value.substring(0, value.indexOf(":")); 
          else 
           prefix = null; 
         } else { 
          continue; 
         } 
        } else { 
         prefix = node.getPrefix(); 
        } 
        namespace = element.lookupNamespaceURI(prefix); 
        if (namespace == null) 
         namespace = ""; 
        namespaces.add(namespace); 
       } 
      } 

     }); 
     traverse(element, new ElementVisitor() { 

      public void visit(Element element) { 
       Set<String> removeLocalNames = new HashSet<String>(); 
       NamedNodeMap attributes = element.getAttributes(); 
       for (int i = 0; i < attributes.getLength(); i++) { 
        Node node = attributes.item(i); 
        if (!XML_NAMESPACE_NAMESPACE.equals(node.getNamespaceURI())) 
         continue; 
        if (namespaces.contains(node.getNodeValue())) 
         continue; 
        removeLocalNames.add(node.getLocalName()); 
       } 
       for (String localName : removeLocalNames) 
        element.removeAttributeNS(XML_NAMESPACE_NAMESPACE, localName); 
      } 

     }); 
    } 

    private final void traverse(Element element, ElementVisitor visitor) { 
     visitor.visit(element); 
     NodeList children = element.getChildNodes(); 
     for (int i = 0; i < children.getLength(); i++) { 
      Node node = children.item(i); 
      if (node.getNodeType() != Node.ELEMENT_NODE) 
       continue; 
      traverse((Element) node, visitor); 
     } 
    } 

} 
0

我一直的方式發現讓JAXB刪除ns2前綴是爲了在xs:schema元素中包含以下屬性:elementFormDefault =「qualified」。因此,這將是這個樣子:

<xs:schema targetNamespace="urn:blah:blah" xmlns="urn:blah:blah" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> 
0

爬行許多職位後,將溶液用NamespacePrefixMapper對JDK版本依賴(這可能會破壞在未來的代碼)或XML DOM樹的操作看起來很複雜。

我的蠻力方法是操縱生成的XML本身。

/** 
* Utility method to hide unused xmlns definition in XML root. 
* @param sXML Original XML string. 
* @return 
*/ 
public static String hideUnUsedNamespace(String sXML) { 
    int iLoc0 = sXML.indexOf("?><"); 
    int iLoc1 = sXML.indexOf("><",iLoc0+3)+1; 
    String sBegin = sXML.substring(0,iLoc0+2); 
    String sHeader = sXML.substring(iLoc0+2, iLoc1-1); 
    String sRest = sXML.substring(iLoc1); 
    //System.out.println("sBegin=" + sBegin); 
    //System.out.println("sHeader=" + sHeader); 
    //System.out.println("sRest=" + sRest); 

    String[] saNS = sHeader.split(" "); 
    //System.out.println("saNS=" + java.util.Arrays.toString(saNS)); 

    StringBuffer sbHeader = new StringBuffer(); 
    for (String s: saNS) { 
     //System.out.println(s); 
     if (s.startsWith("xmlns:")) { 
      String token = "<" + s.substring(6,s.indexOf("=")); 
      //System.out.println("token=" + token + ",indexOf(token)=" + sRest.indexOf(token)); 
      if (sRest.indexOf(token) >= 0) { 
       sbHeader = sbHeader.append(s).append(" "); 
       //System.out.println("...included"); 
      } 
     } else { 
      sbHeader = sbHeader.append(s).append(" "); 
     } 
    } 
    return (sBegin + sbHeader.toString().trim() + ">" + sRest); 
} 

/** 
* Main method for testing 
*/ 
public static void main(String[] args) { 
    String sXML ="<?xml version=\"1.0\" encoding=\"UTF-16\"?><ns2:ebicsRequest xmlns:ns2=\"http://www.ebics.org/H003\" Revision=\"1\" Version=\"H003\" xmlns=\"http://www.ebics.org/H003\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:ns4=\"http://www.ebics.org/S001\" xmlns:ns5=\"http://www.ebics.org/H000\"><ns2:header authenticate=\"true\"><ns2:static><ns2:HostID>SIZBN001</ns2:HostID><ns2:Nonce>A5488F43223063171CA0FA59ADC635F0</ns2:Nonce><ns2:Timestamp>2009-08-04T08:41:56.967Z</ns2:Timestamp><ns2:PartnerID>EBICS</ns2:PartnerID><ns2:UserID>EBIX</ns2:UserID><ns2:Product Language=\"de\">EBICS-Kernel V2.0.4, SIZ/PPI</ns2:Product><ns2:OrderDetails><ns2:OrderType>FTB</ns2:OrderType><ns2:OrderID>A037</ns2:OrderID><ns2:OrderAttribute>OZHNN</ns2:OrderAttribute><ns2:StandardOrderParams/></ns2:OrderDetails><ns2:BankPubKeyDigests><ns2:Authentication Algorithm=\"RSA\" Version=\"X002\">...</ns2:Authentication><ns2:Encryption Algorithm=\"RSA\" Version=\"E002\">...</ns2:Encryption></ns2:BankPubKeyDigests><ns2:SecurityMedium>0000</ns2:SecurityMedium><ns2:NumSegments>1</ns2:NumSegments></ns2:static><ns2:mutable><ns2:TransactionPhase>Initialisation</ns2:TransactionPhase></ns2:mutable></ns2:header><ns2:AuthSignature><ds:SignedInfo><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"/><ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/><ds:Reference URI=\"#xpointer(//*[@authenticate='true'])\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"/></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/><ds:DigestValue>CSbjPbiNcFqSl6lCI1weK5x1nMeCH5bTQq5pedq5uI0=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>...</ds:SignatureValue></ns2:AuthSignature><ns2:body><ns2:DataTransfer><ns2:DataEncryptionInfo authenticate=\"true\"><ns2:EncryptionPubKeyDigest Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\" Version=\"E002\">dFAYe281vj9NB7w+VoWIdfHnjY9hNbZLbHsDOu76QAE=</ns2:EncryptionPubKeyDigest><ns2:TransactionKey>...</ns2:TransactionKey></ns2:DataEncryptionInfo><ns2:SignatureData authenticate=\"true\">...</ns2:SignatureData></ns2:DataTransfer></ns2:body></ns2:ebicsRequest>"; 

    System.out.println("Before=" + sXML); 
    System.out.println("After =" + hideUnUsedNamespace(sXML)); 
} 

的輸出顯示未使用的xmlns命名空間被過濾掉:

<ns2:ebicsRequest xmlns:ns2="http://www.ebics.org/H003" Revision="1" Version="H003" xmlns="http://www.ebics.org/H003" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> 
1

我使用XJC產生我的JAXB類,但SOAP的WebService我使用武力我遵循一些規則,比如不使用名稱空間前綴。

這是無效的:

<envEvento versao="1.00" xmlns="http://www.portalfiscal.inf.br/nfe" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"> 
    <idLote>123</idLote> 
    <evento> 
     <ns2:Signature/> 
    </evento> 
</envEvento> 

這是有效的:

<envEvento versao="1.00" xmlns="http://www.portalfiscal.inf.br/nfe"> 
    <idLote>123</idLote> 
    <evento> 
     <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"/> 
    </evento> 
</envEvento> 

正如指出的那樣,JAXB把命名空間聲明的根元素。

爲了克服這個問題,我使用的第一種方法是避免上下文中不必要的元素。

例如,設置這樣編組的情況下:

JAXBContext.newInstance("path.to.package"); 

可導致JAXB做出的命名空間的一些unncessary聲明。

有時候,我可以得到一個惱人的的xmlns的乘坐=「http://www.w3.org/2000/09/xmldsig#」只設置必要的根元素的背景:

JAXBContext.newInstance(MyRootElement.class); 

當第一個方法不夠時,我使用的第二種方法是讓整個上下文使用相同的名稱空間。

@XmlAttribute(name="xmlns") 
String xmlns = "http://www.w3.org/2000/09/xmldsig#"; 
:只是改變了不必要的「 http://www.w3.org/2000/09/xmldsig#」,在每一個命名空間聲明(如@XmlElement或@XSchema),以允許唯一的命名空間( http://www.portalfiscal.inf.br/nfe

然後,我只是在需要的孩子創建屬性

現在我已經從根目錄中獲得了名稱空間聲明,並且沒有使用任何前綴。