Search code examples
javaxmlxsdsax

XML: Finding namespace uri from xsi:type attribute in SAX content handler


I have an xml document as

<fr:frame xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:fr="http://mebigfatguy.com/ds/frame" 
      xmlns:comp="http://mebigfatguy.com/ds/component"
      xmlns:cont="http://mebigfatguy.com/ds/container"
      xmlns:b="http://mebigfatguy.com/ds/button">
    <comp:preferredSize>500,300</comp:preferredSize>
    <cont:childComponent>
        <cont:name>CENTER</cont:name>
        <cont:component xsi:type="b:Button">
            <comp:name>Click Me</comp:name>
        </cont:component>
    </cont:childComponent>
    <fr:title>Example</fr:title>
</fr:frame>

where b:Button is an xml extension type of cont:component

In my startElement call, i receive a uri of http://mebigfatguy.com/ds/container and qname of cont:component as expected. The xsi:type="b:Button" is found in the attributes, also as expected.

The question I have is how does one lookup the namespace uri of b:Button as retrieved from the xsi:type attribute. Do i have to manage the xmlns attributes manually myself? or is there a built in way to resolve what the uri is?


Solution

  • Yes. you have to do lookup namespace uri for b:Button and you have to manage xmlns attributes using org.xml.sax.helpers.NamespaceSupport. It is little tricky to populate NamespaceSupport properly.

    Below is the sample code which prints xsi:type value with uri and localname:

    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    import org.xml.sax.helpers.NamespaceSupport;
    
    import javax.xml.XMLConstants;
    import javax.xml.parsers.SAXParserFactory;
    
    /**
     * @author Santhosh Kumar Tekuri
     */
    public class XSIHandler extends DefaultHandler{
        private boolean needNewContext;
        private NamespaceSupport nsSupport;
    
        @Override
        public void startDocument() throws SAXException{
            nsSupport = new NamespaceSupport();
            needNewContext = true;
            super.startDocument();
        }
    
        @Override
        public void startPrefixMapping(String prefix, String uri) throws SAXException{
            if(needNewContext){
                nsSupport.pushContext();
                needNewContext = false;
            }
            nsSupport.declarePrefix(prefix, uri);
        }
    
        @Override
        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException{
            if(needNewContext)
                nsSupport.pushContext();
            needNewContext = true;
            String xsiType = atts.getValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type");
            if(xsiType!=null){
                String prefix, suffix;
                int colon = xsiType.indexOf(':');
                if(colon==-1){
                    prefix = "";
                    suffix = xsiType;
                }else{
                    prefix = xsiType.substring(0, colon);
                    suffix = xsiType.substring(colon+1);
                }
                System.out.println("xsi:type for " + qName + " is uri: " + nsSupport.getURI(prefix) + " localName: " + suffix);
            }
        }
    
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException{
            nsSupport.popContext();
        }
    }
    

    here is the driver function:

    public static void main(String[] args) throws Exception{
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.newSAXParser().parse("test.xml", new XSIHandler());
    }
    

    the output is:

    xsi:type for cont:component is uri: http://mebigfatguy.com/ds/button localName: Button