使用 Java XPath 解析 XML 简单字符串

Posted

技术标签:

【中文标题】使用 Java XPath 解析 XML 简单字符串【英文标题】:Parse XML Simple String using Java XPath 【发布时间】:2013-05-18 17:20:02 【问题描述】:

我有这样的 XML 字符串

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

我遵循此帮助

Simplest way to query XML in Java

我的代码:

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



我想获取 msg 节点值但我遇到了异常

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

我不会为这个简单的任务使用一些外部库。请指导我如何获取其他节点的值。 谢谢

【问题讨论】:

如果您需要帮助,请发布您的所有方法... 不要指望只分享一行代码就能得到太多帮助。 【参考方案1】:

您不能将相同的InputSource 重复用于多个evaluate() 调用,因为它会自动关闭。因此,您将收到 Stream closed IO 异常。试试这个

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

编辑: 更好的方法是使用 DocumentBuilderFactory 解析您的 XML 并首先构建一个 Document(使用 JAXP 的 DOM API),然后可以在多个 XPath 评估中重用它。

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

【讨论】:

所以如果我有很多节点,那么我必须为每个节点创建源代码?有没有更好的解决方案? 使用DocumentBuilderFactory 构建一个Document(JAXP DOM API 的一部分),可以在多个 Xpath 评估中重复使用。【参考方案2】:

Ravi's solution也可以表示为:

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

【讨论】:

出于兴趣,如果 XML 字符串是 good, ok 是否只选择逗号之前的字符串中的第一个单词?还是只选择逗号后面的单词? @***man 不支持 Java 的 XPath。您也许可以使用 XPath 2 - 请参阅 Saxon API。【参考方案3】:

你可以试试jcabi-xml,它在后台进行 DOM 操作:

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

【讨论】:

这个库很好,但我不喜欢的是它带来了 aspectj——我认为这不是真的必要 是的,我们计划很快摆脱它 那就太好了!我可以订阅任何 RSS 以使自己保持最新状态? @AlexeyGrigorev 关注/观看这张票:github.com/jcabi/jcabi-xml/issues/61

以上是关于使用 Java XPath 解析 XML 简单字符串的主要内容,如果未能解决你的问题,请参考以下文章

使用 xpath 在 Java 中使用名称空间解析 XML

在 Java 中使用 XPath 解析 XML [重复]

Java DocumentBuilderFactory(javax.xml)通过XPath解析xml文件

XPath对XML文档的解析

Java解析XML汇总(DOM/SAX/JDOM/DOM4j/XPath)

Java XML DOM解析(xPath)