2011-02-02 196 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

然后,我只是在需要的孩子创建属性

现在我已经从根目录中获得了名称空间声明,并且没有使用任何前缀。