java 在java中评估字符串上的xpath并返回结果字符串的简单方法是什么

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4197507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 05:16:19  来源:igfitidea点击:

What's a simple way in java to evaluate an xpath on a string and return a result string

javaxmlxpath

提问by Pablojim

A simple answer needed to a simple question.

一个简单的问题需要一个简单的答案。

For example:

例如:

String xml = "<car><manufacturer>toyota</manufacturer></car>";
String xpath = "/car/manufacturer";
assertEquals("toyota",evaluate(xml, xpath));

How can I write the evaluate method in simple and readable way that will work for any given well-formed xml and xpath.

如何以简单易读的方式编写适用于任何给定格式良好的 xml 和 xpath 的评估方法。

Obviously there are loads of ways this can be achieved but the majority seem very verbose.

显然有很多方法可以实现这一点,但大多数看起来非常冗长。

Any simple ways I'm missing/libraries that can achieve this?

我缺少的任何简单方法/可以实现此目的的库?

For cases where multiple nodes are returned I just want the string representation of this.

对于返回多个节点的情况,我只想要这个的字符串表示。

回答by bdoughan

Here you go, the following can be done with Java SE:

在这里,可以使用 Java SE 完成以下操作:

import java.io.StringReader;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import org.xml.sax.InputSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        String xml = "<car><manufacturer>toyota</manufacturer></car>";
        String xpath = "/car/manufacturer";
        XPath xPath = XPathFactory.newInstance().newXPath();
        assertEquals("toyota",xPath.evaluate(xpath, new InputSource(new StringReader(xml))));
    }

}

回答by vanje

For this use case the XMLUnit library may be a perfect fit: http://xmlunit.sourceforge.net/userguide/html/index.html#Xpath%20Tests

对于这个用例,XMLUnit 库可能是一个完美的选择:http: //xmlunit.sourceforge.net/userguide/html/index.html#Xpath%20Tests

It provides some additional assert methods.

它提供了一些额外的断言方法。

For example:

例如:

assertXpathEvaluatesTo("toyota", "/car/manufacturer",
    "<car><manufacturer>toyota</manufacturer></car>");

回答by odoepner

Using the Xml class from https://github.com/guppy4j/libraries/tree/master/messaging-impl:

使用来自https://github.com/guppy4j/libraries/tree/master/messaging-impl的 Xml 类:

Xml xml = new Xml("<car><manufacturer>toyota</manufacturer></car>");
assertEquals("toyota", xml.get("/car/manufacturer"));

回答by Phlip

I have written assertXPath()in three languages so far. Ruby and Python are the best because they can also parse HTML with its idiosyncrasies via libxml2 and then run XPaths on them. For XML, or for carefully controlled HTML that doesn't have glitches like <for a JavaScript "less than", here's my assertion suite:

assertXPath()到目前为止,我已经用三种语言写过。Ruby 和 Python 是最好的,因为它们还可以通过 libxml2 解析具有其特性的 HTML,然后在它们上运行 XPath。对于 XML,或者对于没有像<JavaScript “小于”这样的小故障的精心控制的 HTML ,这是我的断言套件:

private static final XPathFactory xpathFactory = XPathFactory.newInstance();
private static final XPath xpath = xpathFactory.newXPath();

private static @NonNull Document assertHtml(@NonNull String xml) {
    try {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(false);
            DocumentBuilder builder = factory.newDocumentBuilder();
            ByteArrayInputStream stream = new ByteArrayInputStream(xml.replaceAll("i < len;", "i &lt; len;").getBytes());  //  Because JavaScript ruined HTML's ability to someday be real XML...
            return builder.parse(stream);
        } catch (SAXParseException e) {
            if (e.getLocalizedMessage().startsWith("Unexpected token") && !xml.startsWith("<xml>"))
                return assertHtml("<xml>" + xml + "</xml>");

            throw e;  //  a GOTO to 2 lines down...
        }
    } catch (Throwable e) {
        fail(e.getLocalizedMessage());
    }
    return null;
}

private static @NonNull List<String> assertXPaths(@NonNull Node node, @NonNull String xpathExpression)
{
    NodeList nodes = evaluateXPath(node, xpathExpression);
    List<String> values = new ArrayList<>();

    if (nodes != null)
        for (int i = 0; i < nodes.getLength(); i++) {
            Node item = nodes.item(i);
              //  item.getTextContent();
              //  item.getNodeName();
            values.add(item.getNodeValue());
        }

    if (values.size() == 0)
        fail("XPath not found: " + xpathExpression + "\n\nin: " + nodeToString(node) + "\n");

    return values;
}

private static @NonNull Node assertXPath(@NonNull Node node, @NonNull String xpathExpression)
{
    NodeList nodes = evaluateXPath(node, xpathExpression);

    if (nodes != null && nodes.getLength() > 0)
        return nodes.item(0);

    fail("XPath not found: " + xpathExpression + "\n\nin: " + nodeToString(node) + "\n");
    return null;  //  this can't happen
}

private static NodeList evaluateXPath(@NonNull Node node, @NonNull String xpathExpression) {
    NodeList nodes = null;

    try {
        XPathExpression expr = xpath.compile(xpathExpression);
        nodes = (NodeList) expr.evaluate(node, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
        fail(e.getLocalizedMessage());
    }
    return nodes;
}

private static void assertXPath(Node node, String xpathExpression, String reference) {
    List<String> nodes = assertXPaths(node, xpathExpression);
    assertEquals(1, nodes.size());  // CONSIDER  decorate these assertion diagnostics with nodeToString(). And don't check for one text() - join them all together
    assertEquals(reference, nodes.get(0).trim());  // CONSIDER  same complaint:  We need to see the nodeToString() here
}

private static void refuteXPath(@NonNull Node node, @NonNull String xpathExpression) {
    NodeList nodes = evaluateXPath(node, xpathExpression);

    if (nodes.getLength() != 0)
        fail("XPath should not be found: " + xpathExpression);  // CONSIDER  decorate this with the contents of the node
}

private static @NonNull String nodeToString(@NonNull Node node) {
    StringWriter sw = new StringWriter();
    Transformer t = null;

    try {
        t = TransformerFactory.newInstance().newTransformer();
    } catch (TransformerConfigurationException e) {
        fail(e.getLocalizedMessage());
    }

    t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    t.setOutputProperty(OutputKeys.INDENT, "yes");

    try {
        t.transform(new DOMSource(node), new StreamResult(sw));
    } catch (TransformerException e) {
        fail(e.getLocalizedMessage());
    }

    return sw.toString();
}

Use those recursively, like this:

递归地使用它们,如下所示:

    Document doc = assertHtml(myHtml);
    Node blockquote = assertXPath(doc, "//blockquote[ 'summary_7' = @id ]");

    assertXPath(blockquote, ".//span[ contains(., 'Mammal') and strong/text() = 'anteater' ]");

The benefit of finding a node, then asserting a path relative to that node (via .//) is at failure time nodeToString()will only report the node contents, such as my <blockquote>. The assertion diagnostic message won't contain the entire document, making it very easy to read.

找到一个节点,然后断言相对于该节点的路径(via .//)的好处是在失败时nodeToString()只会报告节点内容,例如 my <blockquote>. 断言诊断消息不会包含整个文档,因此很容易阅读。