Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controlling namespace prefixes in JAXB

How does jaxb determine the list of namespace prefix declarations whem marshalling an object? I used xjc to compile java classes for ebics (ebics schema). When I create an instance for an ebicsRequest it looks like this:


<?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>

I have used a custom NamespacePrefixMapper to declare the default namespace and prefixes for ds and xsi. For the namespace ds it works fine. But for the default namespace it does not. It is declared two times once as ns2 and once as "" the latter coming from my custom NamespacePrefixMapper.getPreDeclaredNamespaceUris. I have played around a lot with this class. Also I tried to use the package-info.java but I was not able to make jaxb use "http://www.ebics.org/H003" as default namespace. What I also do not understand is the appearance of ns4 and ns5 which are not at all part of the xml document.

My NamespacePrefixMapper class looks like


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();
    }


}

I am using


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
like image 951
Wilberforce Avatar asked Feb 02 '11 10:02

Wilberforce


People also ask

How do I set namespace prefix in JAXB?

If you are using the default JAXB implementation provided with Java 6 or later, you can configure the namespace prefixes by extending the NamespacePrefixMapper and setting a property to tell the marshaller to use your extension.

How do you define namespace prefix in XSD?

The namespace can be defined by an xmlns attribute in the start tag of an element. The namespace declaration has the following syntax. xmlns:prefix="URI".

What is JAXB Marshaller?

JAXB provides a fast and convenient way to marshal (write) Java objects into XML and unmarshal (read) XML into objects. It supports a binding framework that maps XML elements and attributes to Java fields and properties using Java annotations.


3 Answers

JAXB always adds all namespaces that are known by the JAXBContext to the root element of the XML document for performance reasons. See this comment by Kohsuke on JAXB-103 for more information.

The only way I found to deal with this, is to traverse the document myself after it has been created with JAXB and remove all unused namespaces using the following helper class:

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);
        }
    }

}
like image 117
Reboot Avatar answered Oct 13 '22 02:10

Reboot


EclipseLink JAXB (MOXy) uses the prefixes as specified in the @XmlSchema annotation (I'm the MOXy lead). Check out my answer to a similar question for an example:

  • How to customize namespace prefixes on Jersey(JAX-WS)
like image 30
bdoughan Avatar answered Oct 13 '22 02:10

bdoughan


The way I've found to get JAXB to remove the ns2 prefix is to include the following attribute in the xs:schema element: elementFormDefault="qualified". So it would look something like this:

<xs:schema targetNamespace="urn:blah:blah" xmlns="urn:blah:blah" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
like image 23
MeplatMasher Avatar answered Oct 13 '22 04:10

MeplatMasher