Wednesday, 28 December 2011

Java XML Parsers


Today we would talk about common used XML parsers available in Java. They are:
1)      DOM
2)      STAX
3)      SAX

Sample XML file:

<?xml version="1.0"?><TDRS><TDR><Date>2011-12-6</Date><Time>18:38:1.47</Time><Timestamp>1323196681047</Timestamp><SystemID>TE06</SystemID><TransactionID>425425678</TransactionID><CorrelationID>none</CorrelationID><SCFTransID></SCFTransID><DeveloperApplication></DeveloperApplication><Company></Company><AppId></AppId><Environment></Environment><Bundle></Bundle><AuthMethod></AuthMethod><AuthToken></AuthToken><HTTPMethod>GET</HTTPMethod><APIType></APIType><SLT></SLT><API></API><APIAction>/</APIAction><SOAPAction></SOAPAction><ClientRequestTimestamp>0</ClientRequestTimestamp><APIAppTimestamp>1323196681040</APIAppTimestamp><APITargetRequestTimestamp>0</APITargetRequestTimestamp><APITargetResponseTimestamp>0</APITargetResponseTimestamp><ClientResponseTimestamp>0</ClientResponseTimestamp><Cached>0</Cached><EventType>Error</EventType><ClientEndpoint></ClientEndpoint><ClientIP>10.155.1.233</ClientIP><LocalIP></LocalIP><LocalPort>23394</LocalPort><TargetEndPoint></TargetEndPoint><TargetIP></TargetIP><StatusCode></StatusCode><ProviderCode></ProviderCode><HTTPStatusCode>403</HTTPStatusCode><Multiplier></Multiplier><Cost></Cost><QuotaAction></QuotaAction><OverQuota>0</OverQuota><OverDayQuota>0</OverDayQuota><OverWeekQuota>0</OverWeekQuota><OverMonthQuota>0</OverMonthQuota><PayloadSize>0</PayloadSize><TDRIdentifier>426561727188bae37b32990ee41</TDRIdentifier></TDR></TDRS>


1)      DOM Parser

§         It parses an entire XML document and load it into memory, modeling it with Object for easy traversal or manipulation.
§         Once the XML loaded in the memory then user can traverse in any direction i.e. forward or backward.
§         DOM Parser is slow and consume a lot of memory if it load a XML document which contains a lot of data, So should be used  for specific purpose (Traversing) only


  I.      DOM Parsing Only

private int domParsing (final String filename) throws SAXException, IOException, JaxenException {
        final int Records = 0;        final
String textfile = FileUtils.readFileToString
(new File (filename));        if ((this.dbf == null) || (this.db == null)) {            this.dbf = DocumentBuilderFactory.newInstance ();            try {                this.db = this.dbf.newDocumentBuilder ();            } catch (final ParserConfigurationException e) {            }        }
        if ((this.dbf != null) && (this.db != null)) {            this.doc = this.db.parse (new InputSource (new StringReader (textfile)));            this.doc.getDocumentElement ().normalize ();//            final Element e = this.doc.getDocumentElement ();
            final List <Node> tdrNodes = new DOMXPath ("TDRS").selectNodes (this.doc);            Long time = 0L;            for (int nodeIdx = 0; nodeIdx < tdrNodes.size (); ++nodeIdx) {                final Node tdrNode = tdrNodes.get (nodeIdx);                while (tdrNode.hasChildNodes ()) {                    final StringBuffer sb = new StringBuffer ();                    final Node childNode = tdrNode.getFirstChild ();                    tdrNode.removeChild (childNode);                    if (childNode.getNodeType () != 1) {                        continue;                    }                    final Long start = System.currentTimeMillis ();                    sb.append (childNode.getNodeName () + ":" + childNode.getNodeValue () + ";");
                    while (childNode.hasChildNodes ()) {
                        final Node grandchildNode = childNode.getFirstChild ();                        sb.append (grandchildNode.getNodeName () + ":" + grandchildNode.getTextContent () + ";");                        childNode.removeChild (grandchildNode);                    }
                    System.out.println (childNode.getNodeName () + "::" + sb.toString ());                    final Long end = System.currentTimeMillis ();                    time = time + (end - start);                    // System.out.println ("Time::" + (end - start) + " Map::" + params);                }                System.out.println ("Total Dom Time:" + time);            }        }        return Records;}


The output is:

TDR::TDR:null;Date:2011-12-6;Time:18:38:1.47;Timestamp:1323196681047;SystemID:TE06;TransactionID:425425678;CorrelationID:none;SCFTransID:;DeveloperApplication:;Company:;AppId:;Environment:;Bundle:;AuthMethod:;AuthToken:;HTTPMethod:GET;APIType:;SLT:;API:;APIAction:/;SOAPAction:;ClientRequestTimestamp:0;APIAppTimestamp:1323196681040;APITargetRequestTimestamp:0;APITargetResponseTimestamp:0;ClientResponseTimestamp:0;Cached:0;EventType:Error;ClientEndpoint:;ClientIP:10.155.1.233;LocalIP:;LocalPort:23394;TargetEndPoint:;TargetIP:;StatusCode:;ProviderCode:;HTTPStatusCode:403;Multiplier:;Cost:;QuotaAction:;OverQuota:0;OverDayQuota:0;OverWeekQuota:0;OverMonthQuota:0;PayloadSize:0;TDRIdentifier:426561727188bae37b32990ee41;



II.      DOM Validation with external XSD


private boolean validateDOM (final String xmlFile, final String xsdFile) {
        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance ();
        factory.setValidating (false);
        factory.setNamespaceAware (true);

        final SchemaFactory schemaFactory = SchemaFactory.newInstance ("http://www.w3.org/2001/XMLSchema");

        try {
            factory.setSchema (schemaFactory.newSchema (new Source[] {new StreamSource (xsdFile)}));
            final DocumentBuilder builder = factory.newDocumentBuilder ();

            builder.setErrorHandler (new ErrorHandler () {

                @Override
                public void warning (final SAXParseException arg0) throws SAXException {
                    // TODO Auto-generated method stub
                    try {
                        throw new Exception ();
                    } catch (final Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace ();
                    }
                }

                @Override
                public void fatalError (final SAXParseException arg0) throws SAXException {
                    // TODO Auto-generated method stub

                }

                @Override
                public void error (final SAXParseException arg0) throws SAXException {
                    // TODO Auto-generated method stub

                }
            });

            final Document document = builder.parse (new InputSource (xmlFile));
        } catch (final SAXException e) {
            e.printStackTrace ();
            return false;
        } catch (final ParserConfigurationException e) {
            e.printStackTrace ();
            return false;
        } catch (final IOException e) {
            e.printStackTrace ();
            return false;
        }
        return true;

    }




2)      STAX Parser – A Pull Parser

§         It’s a Pull Parser
§         Events are generated by the parsing application, thus providing parsing regulation to the client, rather than the parser
§         Developer can control the parsing i.e. you can suspend parsing, skip elements while parsing, and parse multiple documents
§         Parsing events gets generated as the XML document gets parsed


    I.      STAX Parsing Only

private void staxParsing (final String filename) {
        try {
            final FileInputStream fis = new FileInputStream (filename);
            // URL u = new URL(filename);
            // InputStream in = u.openStream();
            final XMLInputFactory factory = XMLInputFactory.newInstance ();
            final XMLStreamReader parser = factory.createXMLStreamReader (fis);

            HashMap <String, ValueTypePair> params = null;
            String NodeName = null;
            Class NodeType = null;
            for (int event = parser.next (); event != XMLStreamConstants.END_DOCUMENT; event = parser.next ()) {
                switch (event) {
                    case XMLStreamConstants.START_ELEMENT:
                        // System.out.println (parser.getLocalName ());
                        if ((parser.getLocalName () != null) && parser.getLocalName ().equalsIgnoreCase ("TDR")) {
                            params = new HashMap <String, ValueTypePair> ();
                        }
                        final int iIndex = this.alBillingXPaths.indexOf (parser.getLocalName ());
                        if (iIndex < 0) {
                            break;
                        }
                        NodeName = this.alBillingColumns.get (iIndex);
                        NodeType = this.alBillingClasses.get (iIndex);
                        break;
                    case XMLStreamConstants.END_ELEMENT:
                        if ((parser.getLocalName () != null) && parser.getLocalName ().equalsIgnoreCase ("TDR")) {
                            // System.out.println (params);
                        }
                        break;
                    case XMLStreamConstants.CHARACTERS:
                        if ((NodeName != null) && (params != null)) {
                            params.put (NodeName, new ValueTypePair (parser.getText (), NodeType));
                        }
                        break;
                    case XMLStreamConstants.CDATA:
                        if ((NodeName != null) && (params != null)) {
                            params.put (NodeName, new ValueTypePair (parser.getText (), NodeType));
                        }
                        break;
                }
            }
            parser.close ();
        } catch (final XMLStreamException ex) {
            System.out.println (ex);
        } catch (final IOException ex) {
            System.out.println ("IOException while parsing " + filename);
            ex.printStackTrace ();
        }
    }



    II.      STAX Validation

STAX Does not support Validation, which acts as its drawback.



3)      SAX Parser – A Push Parser

§         SAX Parser is a push parser
§         it’s parser which call the code & user don’t have full control over it
§         User can not stop the parsing in between
§         Its supports XSD validation



   I.      SAX Parsing Only

private static void saxParsing (final String filename) {
        try {

            final SAXParserFactory factory = SAXParserFactory.newInstance ();
            final SAXParser saxParser = factory.newSAXParser ();

            final DefaultHandler handler = new DefaultHandler () {

               
                @Override
                public void startElement (final String uri,
                                          final String localName,
                                          final String qName,
                                          final Attributes attributes) throws SAXException {

                   
                }

                @Override
                public void endElement (final String uri, final String localName, final String qName) throws SAXException {
                }

                @Override
                public void characters (final char ch[], final int start, final int length) throws SAXException {
                   
                }

            };

            saxParser.parse (filename, handler);

        } catch (final Exception e) {
            e.printStackTrace ();
        }

        System.out.println ("SAX Parsing ends");

    }



II.      SAX Validation & Parsing


private static boolean validateSAX (final String xmlFile, final String xsdFile) {
        final SAXParserFactory factory = SAXParserFactory.newInstance ();
        factory.setValidating (false);
        factory.setNamespaceAware (true);

        final SchemaFactory schemaFactory = SchemaFactory.newInstance ("http://www.w3.org/2001/XMLSchema");

        try {
            if (xsdFile != null) {
                factory.setSchema (schemaFactory.newSchema (new Source[] {new StreamSource (xsdFile)}));
            }
            final SAXParser parser = factory.newSAXParser ();

            final XMLReader reader = parser.getXMLReader ();
            reader.setErrorHandler (new ErrorHandler () {

                @Override
                public void warning (final SAXParseException arg0) throws SAXException {
                    // TODO Auto-generated method stub
                    try {
                        System.err.println ("warning::" + arg0);
                        throw new Exception ();
                    } catch (final Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace ();
                    }
                }

                @Override
                public void fatalError (final SAXParseException arg0) throws SAXException {
                    // TODO Auto-generated method stub
                    System.err.println ("fatal Error::" + arg0);

                }

                @Override
                public void error (final SAXParseException arg0) throws SAXException {
                    // TODO Auto-generated method stub
                    System.err.println ("Error::" + arg0.getMessage ());

                }
            });
            reader.parse (new InputSource (xmlFile));
        } catch (final SAXException e) {
            e.printStackTrace ();
            return false;
        } catch (final ParserConfigurationException e) {
            e.printStackTrace ();
            return false;
        } catch (final IOException e) {
            e.printStackTrace ();
            return false;
        }

        System.out.println ("SAX Parsing ends");

        return true;
    }




4)      Analysis


The xml file is first being validated using the xsd file & then forwarded for parsing, the parsing results are as

(result are generated by doing parsing through eclipse having a hepsize of 512 mb )

SNo.
# TDRS Nodes
Time: Dom Parser (ms)
Time: Stax Parser
(ms)
Time: SAX Parser
(ms)
1
10,000
6866
500
469
2
25,000
Out of Memory
1234
1118
3
50,000
Out of Memory
2422
2328
4
1,00,000
Out of Memory
4906
4672


Hence we conclude that SAX & STAX parsers are much more efficient than the DOM parsers & should be used if to & fro navigation not required in the XML