Jump to: navigation, search

Difference between revisions of "EclipseLink/Release/2.4.0/JAXB RI Extensions/Namespace Prefix Mapper"

 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
<div style="margin:5px;float:right;border:1px solid #000000;padding:5px">__TOC__</div>
 
<div style="margin:5px;float:right;border:1px solid #000000;padding:5px">__TOC__</div>
  
= Design Documentation: NamespacePrefixMapper =
+
= Namespace Prefix Mapper =
  
[http://bugs.eclipse.org/357266 ER 357266]
+
In the current JAXB RI, developed by Sun, there is a series of "proprietary" JAXB extensions which provide advanced functionality outside of the JAXB specification (these extension classes and properties reside in the '''com.sun.xml.bind''' package).
 
+
In the current JAXB RI, developed by Sun, there are a series of "proprietary" JAXB extensions that are available to provide advanced JAXB functionality outside of the JAXB spec (these extension classes reside in the '''com.sun.xml.bind''' package).
+
  
 
The '''NamespacePrefixMapper''' is one of these extensions - it allows the user to customize the namespaces and namespace prefixes used by a given Marshaller. By subclassing the abstract '''NamespacePrefixMapper''' class and setting an instance on the Marshaller, the user can control how prefixes are assigned.  
 
The '''NamespacePrefixMapper''' is one of these extensions - it allows the user to customize the namespaces and namespace prefixes used by a given Marshaller. By subclassing the abstract '''NamespacePrefixMapper''' class and setting an instance on the Marshaller, the user can control how prefixes are assigned.  
 
This document will outline the design for an EclipseLink equivalent to this extension.
 
  
  
Line 25: Line 21:
 
'''NamespacePrefixMapper''' contains the following methods that can be overridden in the subclass:
 
'''NamespacePrefixMapper''' contains the following methods that can be overridden in the subclass:
  
'''public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix)''' (required)
+
'''<code>public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix)</code>''' (required)
 
* Returns a namespace prefix for the given namespaceUri
 
* Returns a namespace prefix for the given namespaceUri
  
'''public String[] getPreDeclaredNamespaceUris()''' (optional)
+
'''<code>public String[] getPreDeclaredNamespaceUris()</code>''' (optional)
 
* Returns any namespaces that should be declared in the document, even if they are not used.  Namespaces returned will be assigned a generated prefix (i.e. "ns1", "ns2", etc)
 
* Returns any namespaces that should be declared in the document, even if they are not used.  Namespaces returned will be assigned a generated prefix (i.e. "ns1", "ns2", etc)
 
* Returned String[] is in the format { "namespace1", "namespace2", ... }
 
* Returned String[] is in the format { "namespace1", "namespace2", ... }
  
'''public String[] getPreDeclaredNamespaceUris2()''' (optional)
+
'''<code>public String[] getPreDeclaredNamespaceUris2()</code>''' (optional)
 
* Returns any namespace prefix/uri pairs that should be declared in the document, even if they are not used.
 
* Returns any namespace prefix/uri pairs that should be declared in the document, even if they are not used.
 
* Returned String[] is in the format { "prefix1", "namespace1", "prefix2", "namespace2", ... }
 
* Returned String[] is in the format { "prefix1", "namespace1", "prefix2", "namespace2", ... }
  
'''public String[] getContextualNamespaceDecls()''' (optional)
+
'''<code>public String[] getContextualNamespaceDecls()</code>''' (optional)
* Returns any namespace prefix/uri pairs that should be used in the document, but not declared in the root of the XML document.  This is useful if the XML being marshalled is part of a larger document, which would have these namespaces declared higher up in the document.
+
* Returns any namespace prefix/uri pairs that should be used in the document, but not actually declared in the root of the XML.  This is useful if the XML being marshalled is part of a larger document, which would have these namespaces declared higher up in the document.
 
* Returned String[] is in the format { "prefix1", "namespace1", "prefix2", "namespace2", ... }
 
* Returned String[] is in the format { "prefix1", "namespace1", "prefix2", "namespace2", ... }
  
Line 76: Line 72:
 
      
 
      
 
}
 
}
 
+
</source>
 +
</div>
 +
<div style="width:800px">
 +
<source lang="java">
 
public class SubObject {
 
public class SubObject {
  
Line 192: Line 191:
 
<source lang="text">
 
<source lang="text">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
<rootNS:testObject xmlns:ns1="http://www.acme.com" xmlns:subNS="http://sub.root.com" xmlns:rootNS="http://www.root.com">
+
<rootNS:testObject  
 +
  xmlns:ns1="http://www.acme.com"  
 +
  xmlns:subNS="http://sub.root.com"  
 +
  xmlns:rootNS="http://www.root.com">
 
   <ns1:name>FOO</ns1:name>
 
   <ns1:name>FOO</ns1:name>
 
   <subNS:subObject>
 
   <subNS:subObject>
Line 202: Line 204:
  
 
As this is a simplified API, there is no support for the "Contextual" or "PreDeclared" namespace functionality provided by the '''NamespacePrefixMapper''' class.
 
As this is a simplified API, there is no support for the "Contextual" or "PreDeclared" namespace functionality provided by the '''NamespacePrefixMapper''' class.
 
 
= Requirements =
 
* Provide an interface/abstract class the user can implement to provide custom namespaces.
 
* Provide the following callback methods:
 
** getPreferredPrefix - Whenever creating a prefix for a specific namespace uri, the PrefixMapper should be called to determine what prefix to use.
 
** getPreDeclaredNamespaceUris - Returns a list of prefix/namespace uri mappings that should be declared at the top of this xml document.
 
** getContextualNamespaceDeclarations - Returns a list of namespace declarations that will exist higher up in the document (in the case of marshalling into a SOAP message or something similar) and don't need to be re-declared.
 

Latest revision as of 11:35, 18 June 2012

Namespace Prefix Mapper

In the current JAXB RI, developed by Sun, there is a series of "proprietary" JAXB extensions which provide advanced functionality outside of the JAXB specification (these extension classes and properties reside in the com.sun.xml.bind package).

The NamespacePrefixMapper is one of these extensions - it allows the user to customize the namespaces and namespace prefixes used by a given Marshaller. By subclassing the abstract NamespacePrefixMapper class and setting an instance on the Marshaller, the user can control how prefixes are assigned.


Configuration

To use this feature, the user must create a subclass of the org.eclipse.persistence.oxm.NamespacePrefixMapper class, and then provide an instance of this subclass to the JAXB Marshaller:

marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, new MyPrefixMapper());


NamespacePrefixMapper contains the following methods that can be overridden in the subclass:

public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) (required)

  • Returns a namespace prefix for the given namespaceUri

public String[] getPreDeclaredNamespaceUris() (optional)

  • Returns any namespaces that should be declared in the document, even if they are not used. Namespaces returned will be assigned a generated prefix (i.e. "ns1", "ns2", etc)
  • Returned String[] is in the format { "namespace1", "namespace2", ... }

public String[] getPreDeclaredNamespaceUris2() (optional)

  • Returns any namespace prefix/uri pairs that should be declared in the document, even if they are not used.
  • Returned String[] is in the format { "prefix1", "namespace1", "prefix2", "namespace2", ... }

public String[] getContextualNamespaceDecls() (optional)

  • Returns any namespace prefix/uri pairs that should be used in the document, but not actually declared in the root of the XML. This is useful if the XML being marshalled is part of a larger document, which would have these namespaces declared higher up in the document.
  • Returned String[] is in the format { "prefix1", "namespace1", "prefix2", "namespace2", ... }


Note: EclipseLink also supports the Sun RI's equivalents: subclasses of com.sun.xml.bind.marshaller.NamespacePrefixMapper can also be set on a Marshaller, using the com.sun.xml.bind.namespacePrefixMapper property name.


EclipseLink also supports a simplified way to provide preferred namespace prefixes; instead of a NamespacePrefixMapper instance, you can pass the Marshaller a Map of uris/prefixes to use when marshalling:

Map<String, String> urisToPrefixes = new HashMap<String, String>();
urisToPrefixes.put("http://uri1.example.com", "prefix1");
urisToPrefixes.put("http://uri2.example.com", "prefix2");        
 
marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, urisToPrefixes);


Example 1

This example will use the following object model:

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement(namespace="http://www.root.com")
public class TestObject {
 
    @XmlElement(namespace="http://www.acme.com")
    public String name;
 
    @XmlElement(namespace="http://sub.root.com")
    public SubObject subObject;
 
}
public class SubObject {
 
    public String description;
 
}

Next, a subclass of NamespacePrefixMapper is implemented:

import org.eclipse.persistence.oxm.NamespacePrefixMapper;
 
public class MyPrefixMapper extends NamespacePrefixMapper {
 
    @Override
    public String getPreferredPrefix(String uri, String suggestion, boolean requirePrefix) {
        if (uri.equals("http://www.root.com")) {
            return "rootNS";
        }
        if (uri.equals("http://sub.root.com")) {
            return "subNS";
        }
        return suggestion;
    }
 
    @Override
    public String[] getContextualNamespaceDecls() {
        return new String[] { "acmeNS", "http://www.acme.com" };
    }
 
    @Override
    public String[] getPreDeclaredNamespaceUris() {
        return new String[] { "fooNS", "barNS" };
    }
 
    @Override
    public String[] getPreDeclaredNamespaceUris2() {
        return new String[] { "altNS", "http://alternate.root.com" };
    }
 
}

An instance of this NamespacePrefixMapper is then passed to the JAXB Marshaller:

JAXBContext ctx = JAXBContext.newInstance(new Class[] { TestObject.class, SubObject.class });
Marshaller m = ctx.createMarshaller();
m.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, new MyPrefixMapper());

Now, when a TestObject is marshalled, it will contain the following namespace information:

<?xml version="1.0" encoding="UTF-8"?>
<rootNS:testObject 
    xmlns:subNS="http://sub.root.com" 
    xmlns:rootNS="http://www.root.com" 
    xmlns:ns5="fooNS" 
    xmlns:ns6="barNS" 
    xmlns:altNS="http://alternate.root.com">
    <acmeNS:name>FOO</acmeNS:name>
    <subNS:subObject>
        <description>Sub</description>
    </subNS:subObject>
</rootNS:testObject>

For comparison, without using a custom NamespacePrefixMapper, the same object would marshal as follows:

<?xml version="1.0" encoding="UTF-8"?>
<ns4:testObject
    xmlns:ns2="http://www.acme.com"
    xmlns:ns3="http://sub.root.com"
    xmlns:ns4="http://www.root.com">
    <ns2:name>FOO</ns2:name>
    <ns3:subObject>
        <description>Sub</description>
    </ns3:subObject>
</ns4:testObject>


Example 2

Using the same object model as above, you could instead provide your list of namespaces and preferred prefixes to the Marshaller in a simple Map:

JAXBContext ctx = JAXBContext.newInstance(new Class[] { TestObject.class, SubObject.class });
 
Map<String, String> urisToPrefixes = new HashMap<String, String>();
urisToPrefixes.put("http://www.root.com", "rootNS");
urisToPrefixes.put("http://sub.root.com", "subNS");        
 
Marshaller m = ctx.createMarshaller();
m.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, prefixesToUris);

Using this approach, the marshalled document would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<rootNS:testObject 
   xmlns:ns1="http://www.acme.com" 
   xmlns:subNS="http://sub.root.com" 
   xmlns:rootNS="http://www.root.com">
   <ns1:name>FOO</ns1:name>
   <subNS:subObject>
      <description>Sub</description>
   </subNS:subObject>
</rootNS:testObject>

As this is a simplified API, there is no support for the "Contextual" or "PreDeclared" namespace functionality provided by the NamespacePrefixMapper class.