Parse XML Simple String using Java XPath

77,653

Solution 1

You can't reuse the same InputSource for multiple evaluate() invocations because it's automatically closed. Hence you're getting the Stream closed IO exception. Try this

InputSource source1 = new InputSource(new StringReader(xml));
InputSource source2 = new InputSource(new StringReader(xml));

String msg = xpath.evaluate("/resp/msg", source);
String status = xpath.evaluate("/resp/status", source2);

System.out.println("msg=" + msg + ";" + "status=" + status);

EDIT:
A better approach would be to use a DocumentBuilderFactory to parse your XML and build a Document first (using JAXP's DOM APIs) which can then be reused across several XPath evaluations.

String xml = "<resp><status>good</status><msg>hi</msg></resp>";

InputSource source = new InputSource(new StringReader(xml));

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(source);

XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();

String msg = xpath.evaluate("/resp/msg", document);
String status = xpath.evaluate("/resp/status", document);

System.out.println("msg=" + msg + ";" + "status=" + status);

Solution 2

Ravi's solution can also be expressed as:

String xml = "<resp><status>good</status><msg>hi</msg></resp>";

XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();

InputSource source = new InputSource(new StringReader(xml));
Document doc = (Document) xpath.evaluate("/", source, XPathConstants.NODE);
String status = xpath.evaluate("/resp/status", doc);
String msg = xpath.evaluate("/resp/msg", doc);

System.out.println("status=" + status);
System.out.println("Message=" + msg);

Solution 3

You can try jcabi-xml, which does DOM manipulations behind the scene:

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
XML xml = new XMLDocument("<resp>...</resp>");
String status = xml.xpath("/resp/status/text()").get(0);
Share:
77,653
Waqas Ali
Author by

Waqas Ali

Updated on February 19, 2020

Comments

  • Waqas Ali
    Waqas Ali over 4 years

    I have XML String like this

    <resp><status>good</status><msg>hi</msg></resp>
    

    I follow this help

    Simplest way to query XML in Java

    MyCode:

    public static void main(String args[]) {
    
        String xml = "<resp><status>good</status><msg>hi</msg></resp>";
    
        XPathFactory xpathFactory = XPathFactory.newInstance();
        XPath xpath = xpathFactory.newXPath();
    
        InputSource source = new InputSource(new StringReader(xml));
    
        String status = "";
        String msg = "";
        try {
            status = (String) xpath.evaluate("/resp/status", source,XPathConstants.STRING);
            msg = (String) xpath.evaluate("/resp/msg", source,XPathConstants.STRING);
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        System.out.println("status=" + status);
        System.out.println("Message=" + msg);
    
    
    }
    

    I want to get msg node value but i got exception

    java.io.IOException: Stream closed
    at java.io.StringReader.ensureOpen(StringReader.java:39)
    at java.io.StringReader.read(StringReader.java:73)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1742)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.arrangeCapacity(XMLEntityScanner.java:1619)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.skipString(XMLEntityScanner.java:1657)
    at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:193)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:771)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:107)
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:225)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:283)
    at com.sun.org.apache.xpath.internal.jaxp.XPathImpl.evaluate(XPathImpl.java:468)
    at com.sun.org.apache.xpath.internal.jaxp.XPathImpl.evaluate(XPathImpl.java:515)
    at Parsing.main(Parsing.java:25)--------------- linked to ------------------
    javax.xml.xpath.XPathExpressionException
    at com.sun.org.apache.xpath.internal.jaxp.XPathImpl.evaluate(XPathImpl.java:475)
    at com.sun.org.apache.xpath.internal.jaxp.XPathImpl.evaluate(XPathImpl.java:515)
    at Parsing.main(Parsing.java:25)
    Caused by: java.io.IOException: Stream closed
    at java.io.StringReader.ensureOpen(StringReader.java:39)
    at java.io.StringReader.read(StringReader.java:73)
    at     com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1742)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.arrangeCapacity(XMLEntityScanner.java:1619)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.skipString(XMLEntityScanner.java:1657)
    at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:193)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:771)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:107)
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:225)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:283)
    at com.sun.org.apache.xpath.internal.jaxp.XPathImpl.evaluate(XPathImpl.java:468)
    ... 2 more
    

    I am not going to use some external library for this simple task. please guide me how to get other node's values. Thanks

  • Waqas Ali
    Waqas Ali about 11 years
    so if i have many nodes then i have to create source for each of them? is there any better solution?
  • Ravi K Thapliyal
    Ravi K Thapliyal about 11 years
    Use a DocumentBuilderFactory to build a Document (part of JAXP DOM APIs) that can be reused across multiple Xpath evaluations.
  • Paul Alexander
    Paul Alexander over 10 years
    out of interest, if the XML string was <status>good, ok</status> is there anyway of just selecting the first word in the string before the comma? or just selecting the word after the comma?
  • McDowell
    McDowell over 10 years
    @brookman Not with Java's XPath support. You might be able to with XPath 2 - see the Saxon API.
  • Alexey Grigorev
    Alexey Grigorev over 9 years
    This library is very nice, but what I don't like is that it brings aspectj with it - which I don't think is really necessary
  • yegor256
    yegor256 over 9 years
    Yeah, we're planning to get rid of that very soon
  • Alexey Grigorev
    Alexey Grigorev over 9 years
    that would be very nice! any RSS I can subscribe for to keep myself up-to-date?
  • yegor256
    yegor256 over 9 years
    @AlexeyGrigorev follow/watch this ticket: github.com/jcabi/jcabi-xml/issues/61