40

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

2
  • Post all your method if you want help... Commented May 23, 2013 at 11:20
  • Don't expect to get much help by sharing just one line of code. Commented May 23, 2013 at 11:21

3 Answers 3

58

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", source1);
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);
Sign up to request clarification or add additional context in comments.

2 Comments

so if i have many nodes then i have to create source for each of them? is there any better solution?
Use a DocumentBuilderFactory to build a Document (part of JAXP DOM APIs) that can be reused across multiple Xpath evaluations.
14

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);

2 Comments

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?
@brookman Not with Java's XPath support. You might be able to with XPath 2 - see the Saxon API.
8

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);

4 Comments

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
Yeah, we're planning to get rid of that very soon
that would be very nice! any RSS I can subscribe for to keep myself up-to-date?
@AlexeyGrigorev follow/watch this ticket: github.com/jcabi/jcabi-xml/issues/61

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.