一个简单问题需要一个简单的答案。
例如:
String xml = "<car><manufacturer>toyota</manufacturer></car>";
String xpath = "/car/manufacturer";
assertEquals("toyota",evaluate(xml, xpath));
如何以简单易读的方式编写evaluate方法,该方法适用于任何给定格式良好的xml和xpath。
显然,有很多方法可以实现,但大多数人看起来非常冗长。
我缺少的任何简单方法/可以实现此目的的库?
对于返回多个节点的情况,我只想要这个字符串表示。
答案 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 < 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>
。断言诊断消息不会包含整个文档,因此非常易于阅读。