0

I am trying to parse XML in Java using SAX. For that reason, I made myself a UnitTest.

When I try to parse the XML from a File, everything works wonderfully, but when I try to parse the exact same XML from a String the parsing fails and gives me the following Error:

java.net.MalformedURLException: no protocol: <?xml version="1.0" encoding="UTF-8"?>
<root>                                    
    <subnode>                              
        <id>s1</id>                        
        <name>Subnode one</name>           
        <value>11</value>                  
    </subnode>                             
    <subnode>                              
        <id>s2</id>                        
        <name>Subnode two</name>           
        <value>22</value>                  
    </subnode>                             
</root>                                   
    at java.net.URL.<init>(URL.java:586)
    at java.net.URL.<init>(URL.java:483)
    at java.net.URL.<init>(URL.java:432)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:619)
    at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:189)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:812)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:649)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(SAXParserImpl.java:333)
    at javax.xml.parsers.SAXParser.parse(SAXParser.java:274)
    at tests.xmlparsing.XMLParserTest.ParseStringTest(XMLParserTest.java:89)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

My UnitTest-File: http://pastebin.com/vd2zjWHu

package tests.xmlparsing;

import org.junit.Test;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.io.*;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.*;

import static org.junit.Assert.*;

/**
 * Created by simonlammer on 14.03.15.
 */
public class XMLParserTest {
    private static SAXHandler handler;
    private static String xmlString;
    static {
        handler = new SAXHandler();
        xmlString =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\r\n" +
            "<root>                                    " + "\r\n" +
            "   <subnode>                              " + "\r\n" +
            "       <id>s1</id>                        " + "\r\n" +
            "       <name>Subnode one</name>           " + "\r\n" +
            "       <value>11</value>                  " + "\r\n" +
            "   </subnode>                             " + "\r\n" +
            "   <subnode>                              " + "\r\n" +
            "       <id>s2</id>                        " + "\r\n" +
            "       <name>Subnode two</name>           " + "\r\n" +
            "       <value>22</value>                  " + "\r\n" +
            "   </subnode>                             " + "\r\n" +
            "</root>                                   ";
    }
    private SAXParser parser;

    public XMLParserTest() throws ParserConfigurationException, SAXException {
        this.parser = newSAXParser();
    }

    private SAXParser newSAXParser() throws ParserConfigurationException, SAXException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser(); // throws ParserConfigurationException
        return parser;
    }

    @Test
    public void SAXParserNotNullTest() {
        assertNotNull(parser);
    }

    @Test
    public void SAXHandlerNotNullTest() {
        assertNotNull(handler);
    }

    @Test
    public void ParseFileTest() throws IOException, SAXException {
        // create File
        File file = new File("temporaryXMLFileUsedForUnitTest.xml");
        try {
            FileWriter fw = new FileWriter(file);
            fw.write(xmlString);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            fail("Could not create file");
        }

        // parse
        handler.clearElements();
        parser.parse(file, handler);
        List<SampleElement> elements = handler.getElements();

        // validate
        validateElementList(elements);

        // delete file
        file.delete();
    }

    @Test
    public void ParseStringTest() throws IOException, SAXException {
        // parse
        handler.clearElements();
        parser.parse(xmlString, handler);
        List<SampleElement> elements = handler.getElements();

        // validate
        validateElementList(elements);
    }

    private void validateElementList(List<SampleElement> elements) {
        assertEquals(2,             elements.size());
        assertEquals("s1",          elements.get(0).getId());
        assertEquals("Subnode one", elements.get(0).getName());
        assertEquals(11,            elements.get(0).getValue());
        assertEquals("s2",          elements.get(1).getId());
        assertEquals("Subnode two", elements.get(1).getName());
        assertEquals(22,            elements.get(1).getValue());
    }

    public static class SampleElement {
        private String id;
        private String name;
        private int value;

        public SampleElement(String id, String name, int value) {
            this.id = id;
            this.name = name;
            this.value = value;
        }

        public String getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        public int getValue() {
            return value;
        }
    }

    public static class SAXHandler extends DefaultHandler {
        private LinkedList<SampleElement> elements;
        private String id;
        private String name;
        private boolean tagId = false;
        private boolean tagName = false;
        private boolean tagValue = false;
        private String value;

        public SAXHandler() {
            elements = new LinkedList<>();
        }

        public void clearElements() {
            elements.clear();
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String str = new String(ch, start, length);
            if (tagId) {
                id = str;
            } else if (tagName) {
                name = str;
            } else if (tagValue) {
                value = str;
            }
        }

        public List<SampleElement> getElements() {
            return (LinkedList<SampleElement>) elements.clone();
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (qName.equalsIgnoreCase("id")) {
                tagId = false;
            } else if (qName.equalsIgnoreCase("name")) {
                tagName = false;
            } else if (qName.equalsIgnoreCase("value")) {
                tagValue = false;

                // create new SampleElement
                if (id != null && name != null && value != null) {
                    int val = Integer.parseInt(value);
                    SampleElement element = new SampleElement(id, name, val);
                    elements.add(element);

                    // clear strings
                    id = name = value = null;
                }
            }
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (qName.equalsIgnoreCase("id")) {
                tagId = true;
                id = null;
            } else if (qName.equalsIgnoreCase("name")) {
                tagName = true;
                name = null;
            } else if (qName.equalsIgnoreCase("value")) {
                tagValue = true;
                value = null;
            }
        }
    }
}

Question: Why does it work when parsing from a file but not when parsing from a String? How do I parse from a String using the SAX parser, without receiving this error?

Edit: The solution was found by RealSkeptic, thank you very much. The overload which I used was not supposed to be the XML itself, rather a path to a XML-file. In order to solve the problem, I had to change "parser.parse(xmlString), handler);" to "parser.parse(new InputSource(new StringReader(xmlString)), handler);"

GitProphet
  • 870
  • 1
  • 12
  • 22

1 Answers1

3

You use

parser.parse(xmlString, handler);

But the documentation of SAXParser.parse(String uri,DefaultHandler dh) clearly tells you that the string to pass it is a URI of where the XML actually is. It is not the XML itself!

If you want to read the XML directly from a string, you need to create an InputSource from the string, and then use the SAXParser.parse(InputSource is,DefaultHandler dh) method.

To create an InputSource from a String, you may use

InputSource is = new InputSource( new StringReader( xmlString ) );

StringReader is a special type of Reader that reads from a string (just as it would from an InputStream).

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79