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

时间:2010-11-16 18:16:25

标签: java xml xpath

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

例如:

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

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

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

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

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

4 个答案:

答案 0 :(得分:37)

在这里,您可以使用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))));
    }

}

答案 1 :(得分:7)

对于此用例,XMLUnit库可能非常适合: http://xmlunit.sourceforge.net/userguide/html/index.html#Xpath%20Tests

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

例如:

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

答案 2 :(得分:0)

使用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"));

答案 3 :(得分:0)

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

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

像下面这样递归地使用它们:

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

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

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