Receiving Namespace Mappings


By default, namespace declaration attributes such as xmlns="http://ns.cafeconleche.org/Orders/" and xmlns:xlink="http://www.w3.org/1999/xlink" are not included in the list of attributes passed to startElement() . Instead each such namespace declaration attribute is signaled by a call to startPrefixMapping() immediately before the startElement() call corresponding to the element start-tag where the declaration appears. Furthermore, the endElement() call corresponding to the end-tag of that element is immediately followed by an endPrefixMapping() call.

 public void  startPrefixMapping  (String  prefix,  String  uri  )  throws SAXException public void  endPrefixMapping  (String  prefix  ) throws SAXException 

Ninety percent these events. As long as you only care about URIs and prefixes on element and of the time you can ignore attribute names , there's really no need to pay attention to these methods . You simply inspect the URIs and prefixes passed in the arguments to the startElement() method. However, in a few applicationsincluding XSLT, schemas, and SOAPprefixes appear in attribute values and even element content. If so, you need to keep track of which URIs particular prefixes are bound to and where. This is complicated by the fact that, although many documents declare all namespaces on the root element and most documents map each prefix to exactly one URI, it is possible for namespaces to be declared elsewhere than the root and for one prefix to match different URIs in different parts of the same document. Conversely, one URI can be associated with several prefixes, even in the same part of a document.

The proper way to manage prefix mappings is to keep the prefixes and URIs on a per-document stack. startPrefixMapping() pushes the pair onto the stack. endPrefixMapping() pops them off. [2] If at any time while parsing you need to know the current binding of a prefix you've encountered , simply search down the stack starting with the top. The first binding you encounter for the prefix is the one you want. The org.xml.sax.helpers.NamespaceSupport class implements this logic. It's summarized in Example 6.12.

[2] SAX does state that "start/endPrefixMapping events are not guaranteed to be properly nested relative to each other." Thus you might pop mappings off in a different order than you pushed them, and in so doing pop the wrong mapping at the wrong time. However, as long as you push and pop all mappings as soon as you see a start/endPrefixMapping event, you'll always pop the same set of mappings you pushed , even if in a different order. Thus this turns out not to matter in practice.

Example 6.12 The NamespaceSupport Class
 package org.xml.sax.helpers; public class NamespaceSupport {   public final static String XMLNS    = "http://www.w3.org/XML/1998/namespace";   public NamespaceSupport();   public void        reset();   public void        pushContext();   public void        popContext();   public boolean     declarePrefix(String prefix, String uri);   public String[]    processName(String qualifedName,    String parts[], boolean isAttribute);   public String      getURI(String prefix);   public Enumeration getPrefixes();   public String      getPrefix(String uri);   public Enumeration getPrefixes(String uri);   public Enumeration getDeclaredPrefixes(); } 

To use this class, you simply call pushContext() in the first startPrefixMapping() event before each startElement() , declarePrefix() , and startPrefixMapping() event; and popContext() in the first endPrefixMapping() event after each endElement() event.

You add namespace declarations to a context by passing their prefix and URI as strings to declarePrefix() inside each startPrefixMapping() call. Use the empty string as the prefix to declare the default namespace. Here's the cookbook code you can paste into your content handler to maintain a stack of namespaces:

 private NamespaceSupport namespaces;  private boolean needNewContext = true; public void startDocument() {   namespaces = new NamespaceSupport(); } public void startPrefixMapping(String prefix, String uri) throws SAXException {   if (needNewContext) {     namespaces.pushContext();     needNewContext = false;   }   namespaces.declarePrefix(prefix, uri); } public void endPrefixMapping(String prefix)  throws SAXException {   if (!needNewContext) {     namespaces.popContext();     needNewContext = true;   } } 

The getDeclaredPrefixes() , getPrefix() , getPrefixes() , and getURI() methods all return various information about the namespaces in scope in the current context. For example, the following startElement() method prints the namespace URI for the values of type attributes, such as might be found in a schema:

 public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes atts) throws SAXException {   String value = atts.getValue("type");   if (value != null) {     String prefix = "";     if (value.indexOf(':') >= 0) {       prefix = value.substring(0, value.indexOf(':'));     }     String uri = namespaces.getURI(prefix);   } } 

That point is easy to miss , so let me make it again: the getURI() method is being used here to get the namespace URI for the attribute value, not the attribute itself. The attribute in this example is in no namespace at all. NamespaceSupport and startPrefixMapping() / endPrefixMapping() are relevant only to namespace prefixes in attribute values and element content, not to namespace prefixes used on attribute and element names. If you only care about namespace prefixes in element and attribute names, then you can ignore this section completely.

If you want to reuse a NamespaceSupport for a new document, call reset() , probably from the startDocument() method.

Note

The Namespaces in XML specification explicitly states that the xml prefix is always bound to the URL http://www.w3.org/XML/1998/namespace . Even if this is explicitly declared with an xmlns:xml="http://www.w3.org/XML/1998/namespace" attribute, startPrefixMapping() and endPrefixMapping() are never invoked for the xml prefix. However, this binding is automatically included in all NamespaceSupport objects regardless of context.

Also according to Namespaces in XML, "The prefix xmlns is used only for namespace bindings and is not itself bound to any namespace name ." Thus it too does not cause startPrefixMapping() and endPrefixMapping() invocations.




Processing XML with Java. A Guide to SAX, DOM, JDOM, JAXP, and TrAX
Processing XML with Javaв„ў: A Guide to SAX, DOM, JDOM, JAXP, and TrAX
ISBN: 0201771861
EAN: 2147483647
Year: 2001
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net