Announcement Announcement Module
Collapse
No announcement yet.
Namespace prefix Mapping:Jaxb2Marshaller Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Namespace prefix Mapping:Jaxb2Marshaller

    I used xjc to generate POJOs for a WSDL and that seems to be fine.

    The problem I am running to is when I send the request to Web Service it expects a prefix to one particular namespace exactly like it is defined in WSDL.

    My request XML has namespaces prefixed differently (used MessageTracing to view request/response XMLs). It looks like this:

    Code:
    <?xml version="1.0" encoding="UTF-8" ?> 
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
      <soapenv:Header /> 
     <soapenv:Body>
    <ns1:getAvailabilityRequest xmlns:ns1="http://mydomain/ws/bright/2.0" 
    xmlns:ns2="http://mydomain2/basics/domains/screen/2.0">
     <ns2:ServiceProviderRequestMessage>
    ...................
    ...................
      </ns2:ServiceProviderRequestMessage>
      </ns1:getAvailabilityRequest>
      </soapenv:Body>
      </soapenv:Envelope>
    As you can see the marshaller is prefixing the namespaces as ns1, ns2, etc but the web service provide want them to be with the same names as it's defined in the WSDL. "ns1" should be "bright" and "ns2" should be "screen".

    How can this namespacemapping be defined when using Jaxb2Marshaller?

    If I used CXF I would have done it by using the dataBinding of jaxws but would like to use Spring Jaxb2Marshaller here.

    For example with CXF the following works for me:

    Code:
    <jaxws:dataBinding>
    <bean class="org.apache.cxf.jaxb.JAXBDataBinding">
    <property name="namespaceMap">
    <map>
    <entry>
    <key><value>http://mydomain/ws/bright/2.0</value></key>
    <value>bright</value> 
    </entry>
    <entry>
    <key><value>http://mydomain2/screen/2.0</value></key>
    <value>screen</value> 
    </entry>
    </map>
    </property>
    </bean>
    </jaxws:dataBinding>
    Any help? Thanks.
    Last edited by kitsVA; Mar 8th, 2010, 10:44 AM.

  • #2
    anyone know a solution to this namespacemapping? Thanks.

    This problem is explained here but thinking it should be simple with some configuration in Spring applicationcontext.xml like I was able to do with CXF.

    Comment


    • #3
      Solved this isssue by doing the following:

      added this in applicationcontext.xml under the marshaller/unmarshaller

      <bean id="myMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshalle r">

      Code:
      		<property name="marshallerProperties">
      			<map>
      				<entry key="com.sun.xml.bind.namespacePrefixMapper">
      					<bean id="NamespacePrefixMapperImpl" class="mydomain.utils.NamespacePrefixMapperImpl" />
      				</entry>
      			</map>
      		</property>
      Then I just adding my namespacing mappings by overriding the method getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) below with my mappings.
      Code:
      /* *********************************************************
       * This namespacemapping is needed as the web service expects the bright namespace to be explicitly defined.
       ********************************************************* */
      
      package mydomain.utils;
      
      import java.io.OutputStream;
      
      import javax.xml.stream.XMLEventWriter;
      import javax.xml.stream.XMLStreamWriter;
      import javax.xml.transform.dom.DOMResult;
      
      import org.w3c.dom.Node;
      
      import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
      
      /**
       * Implemented to determine URI -> prefix mapping.
       */
      public class NamespacePrefixMapperImpl extends NamespacePrefixMapper {
      
          private static final String[] EMPTY_STRING = new String[0];
      
          /**
           * Returns a preferred prefix for the given namespace URI.
           * 
           * This method is intended to be overrided by a derived class.
           * 
           * @param namespaceUri
           *      The namespace URI for which the prefix needs to be found.
           *      Never be null. "" is used to denote the default namespace.
           * @param suggestion
           *      When the content tree has a suggestion for the prefix
           *      to the given namespaceUri, that suggestion is passed as a
           *      parameter. Typicall this value comes from the QName.getPrefix
           *      to show the preference of the content tree. This parameter
           *      may be null, and this parameter may represent an already
           *      occupied prefix. 
           * @param requirePrefix
           *      If this method is expected to return non-empty prefix.
           *      When this flag is true, it means that the given namespace URI
           *      cannot be set as the default namespace.
           * 
           * @return
           *      null if there's no prefered prefix for the namespace URI.
           *      In this case, the system will generate a prefix for you.
           * 
           *      Otherwise the system will try to use the returned prefix,
           *      but generally there's no guarantee if the prefix will be
           *      actually used or not.
           * 
           *      return "" to map this namespace URI to the default namespace.
           *      Again, there's no guarantee that this preference will be
           *      honored.
           * 
           *      If this method returns "" when requirePrefix=true, the return
           *      value will be ignored and the system will generate one.
           * 
           * @since JAXB 1.0.1
           */
          @Override
          public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix){
              /*
               * The only namespace that matter is liberty:id-sis-pp:2005-05,
               * which sould be the default namespace
               */
          	if (namespaceUri == null || namespaceUri.equals("")) {
                  return "";
              }
              if (namespaceUri.equalsIgnoreCase("http://mydomain/ws/bright/2.0")){
                  return "bright";
              }   
              if (namespaceUri.equalsIgnoreCase("http://mydomain2/screen/2.0")){
                  return "screen";
              }     
              return suggestion;
          }
      
          /**
           * Returns a list of namespace URIs that should be declared
           * at the root element.
           *
           * <p>
           * By default, the JAXB RI 1.0.x produces namespace declarations only when
           * they are necessary, only at where they are used. Because of this
           * lack of look-ahead, sometimes the marshaller produces a lot of
           * namespace declarations that look redundant to human eyes. For example,
           * <pre><xmp>
           * <?xml version="1.0"?>
           * <root>
           *   <ns1:child xmlns:ns1="urn:foo"> ... </ns1:child>
           *   <ns2:child xmlns:ns2="urn:foo"> ... </ns2:child>
           *   <ns3:child xmlns:ns3="urn:foo"> ... </ns3:child>
           *   ...
           * </root>
           * <xmp></pre>
           *
           * <p>
           * The JAXB RI 2.x mostly doesn't exhibit this behavior any more,
           * as it declares all statically known namespace URIs (those URIs
           * that are used as element/attribute names in JAXB annotations),
           * but it may still declare additional namespaces in the middle of
           * a document, for example when (i) a QName as an attribute/element value
           * requires a new namespace URI, or (ii) DOM nodes as a portion of an object
           * tree requires a new namespace URI.
           *
           * <p>
           * If you know in advance that you are going to use a certain set of
           * namespace URIs, you can override this method and have the marshaller
           * declare those namespace URIs at the root element.
           *
           * <p>
           * For example, by returning <code>new String[]{"urn:foo"}</code>,
           * the marshaller will produce:
           * <pre><xmp>
           * <?xml version="1.0"?>
           * <root xmlns:ns1="urn:foo">
           *   <ns1:child> ... </ns1:child>
           *   <ns1:child> ... </ns1:child>
           *   <ns1:child> ... </ns1:child>
           *   ...
           * </root>
           * <xmp></pre>
           * <p>
           * To control prefixes assigned to those namespace URIs, use the
           * {@link #getPreferredPrefix(String, String, boolean)} method. 
           * 
           * @return
           *      A list of namespace URIs as an array of {@link String}s.
           *      This method can return a length-zero array but not null.
           *      None of the array component can be null. To represent
           *      the empty namespace, use the empty string <code>""</code>.
           * 
           * @since
           *      JAXB RI 1.0.2 
           */
          @Override
          public String[] getPreDeclaredNamespaceUris() {
              return EMPTY_STRING;
          }
      
          /**
           * Similar to {@link #getPreDeclaredNamespaceUris()} but allows the
           * (prefix,nsUri) pairs to be returned.
           *
           * <p>
           * With {@link #getPreDeclaredNamespaceUris()}, applications who wish to control
           * the prefixes as well as the namespaces needed to implement both
           * {@link #getPreDeclaredNamespaceUris()} and {@link #getPreferredPrefix(String, String, boolean)}.
           *
           * <p>
           * This version eliminates the needs by returning an array of pairs.
           *
           * @return
           *      always return a non-null (but possibly empty) array. The array stores
           *      data like (prefix1,nsUri1,prefix2,nsUri2,...) Use an empty string to represent
           *      the empty namespace URI and the default prefix. Null is not allowed as a value
           *      in the array.
           *
           * @since
           *      JAXB RI 2.0 beta
           */
          @Override
          public String[] getPreDeclaredNamespaceUris2() {
              return EMPTY_STRING;
          }
      
          /**
           * Returns a list of (prefix,namespace URI) pairs that represents
           * namespace bindings available on ancestor elements (that need not be repeated
           * by the JAXB RI.)
           *
           * <p>
           * Sometimes JAXB is used to marshal an XML document, which will be
           * used as a subtree of a bigger document. When this happens, it's nice
           * for a JAXB marshaller to be able to use in-scope namespace bindings
           * of the larger document and avoid declaring redundant namespace URIs.
           *
           * <p>
           * This is automatically done when you are marshalling to {@link XMLStreamWriter},
           * {@link XMLEventWriter}, {@link DOMResult}, or {@link Node}, because
           * those output format allows us to inspect what's currently available
           * as in-scope namespace binding. However, with other output format,
           * such as {@link OutputStream}, the JAXB RI cannot do this automatically.
           * That's when this method comes into play.
           *
           * <p>
           * Namespace bindings returned by this method will be used by the JAXB RI,
           * but will not be re-declared. They are assumed to be available when you insert
           * this subtree into a bigger document.
           *
           * <p>
           * It is <b>NOT</b> OK to return  the same binding, or give
           * the receiver a conflicting binding information.
           * It's a responsibility of the caller to make sure that this doesn't happen
           * even if the ancestor elements look like:
           * <pre><xmp>
           *   <foo:abc xmlns:foo="abc">
           *     <foo:abc xmlns:foo="def">
           *       <foo:abc xmlns:foo="abc">
           *         ... JAXB marshalling into here.
           *       </foo:abc>
           *     </foo:abc>
           *   </foo:abc>
           * </xmp></pre>
           * <!-- TODO: shall we relax this constraint? -->
           *
           * @return
           *      always return a non-null (but possibly empty) array. The array stores
           *      data like (prefix1,nsUri1,prefix2,nsUri2,...) Use an empty string to represent
           *      the empty namespace URI and the default prefix. Null is not allowed as a value
           *      in the array.
           *
           * @since JAXB RI 2.0 beta
           */
          @Override
          public String[] getContextualNamespaceDecls() {
              return EMPTY_STRING;
          }
      
      }

      Comment


      • #4
        Thanks, very helpful as I have the exact same problem.

        Comment


        • #5
          Thanks kitsVA for the example code. This works great!

          The following code is a little less specific and might give you an even more easy start

          Code:
            <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
              <property name="marshallerProperties">
                <map>
                  <entry key="com.sun.xml.bind.namespacePrefixMapper" value-ref="namespacePrefixMapper" />
                </map>
              </property>
            </bean>
          
            <bean id="namespacePrefixMapper" class="mydomain.utils.ConfigurableNamespacePrefixMapperImpl">
              <property name="mapping">
                <map>
                  <entry>
                    <key>
                      <value>http://mydomain/ws/bright/2.0</value>
                    </key>
                    <value>bright</value>
                  </entry>
                  <entry>
                    <key>
                      <value>http://mydomain2/screen/2.0</value>
                    </key>
                    <value>screen</value>
                  </entry>
                </map>
              </property>
            </bean>
          Code:
          package mydomain.utils;
          
          import java.util.Map;
          
          import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
          
          /**
           * Maps the namespaces to configured prefixes (if any).
           */
          public class ConfigurableNamespacePrefixMapperImpl extends NamespacePrefixMapper {
          
              private Map<String, String> mapping;
          
              /*
               * (non-Javadoc)
               * 
               * @see
               * com.sun.xml.bind.marshaller.NamespacePrefixMapper#getPreferredPrefix(java.lang.
               * String, java.lang.String, boolean)
               */
              @Override
              public String getPreferredPrefix(final String namespaceUri, final String suggestion,
                      final boolean requirePrefix) {
          
                  String prefix = null;
                  if (namespaceUri == null || namespaceUri.equals("")) {
                      prefix = "";
                  }
                  if (mapping != null)
                      for (String uri : mapping.keySet()) {
                          if (namespaceUri.equalsIgnoreCase(uri)) {
                              prefix = mapping.get(uri);
                              break;
                          }
                      }
                  if (prefix == null) {
                      prefix = suggestion;
                  }
                  return prefix;
              }
          
              public final void setMapping(Map<String, String> mapping) {
                  this.mapping = mapping;
              }
          
          }
          Cheers,
          - Marc
          Last edited by jandel; Aug 12th, 2010, 02:39 AM.

          Comment

          Working...
          X