仅使用XPath在VTD-XML中进行动态查找
我试图使用XPath表达式在VTD-XML中查找引用当前元素的元素。假设我的XML包含书籍和评级,如下所示:仅使用XPath在VTD-XML中进行动态查找,xml,xpath,vtd-xml,Xml,Xpath,Vtd Xml,我试图使用XPath表达式在VTD-XML中查找引用当前元素的元素。假设我的XML包含书籍和评级,如下所示: <root> <book id="1" name="Book1"/> <book id="2" name="Book1"/> <rating book-id="1" value="5"/> <rating book-id="2" value="3"/> </root> @Test public vo
<root>
<book id="1" name="Book1"/>
<book id="2" name="Book1"/>
<rating book-id="1" value="5"/>
<rating book-id="2" value="3"/>
</root>
@Test
public void xpathReference() throws Exception {
byte[] bytes = ("<root>\n"
+ " <book id=\"1\" name=\"Book1\"/>\n"
+ " <book id=\"2\" name=\"Book1\"/>\n"
+ " <rating book-id=\"1\" value=\"5\"/>\n"
+ " <rating book-id=\"2\" value=\"3\"/>\n"
+ " <rating book-id=\"3\" value=\"4\"/>\n"
+ "</root>").getBytes();
VTDGen vtdGenerator = new VTDGen();
vtdGenerator.setDoc(bytes);
vtdGenerator.parse(true);
VTDNav vtdNavigator = vtdGenerator.getNav();
AutoPilot autoPilot = new AutoPilot(vtdNavigator);
autoPilot.selectXPath("//*/rating[./@book-id=//book/@id]/@value");
int id;
int count = 0;
while ((id = autoPilot.evalXPath()) != -1) {
String elementName = vtdNavigator.toString(id);
int text = vtdNavigator.getAttrVal(elementName);
String txt = text != -1 ? vtdNavigator.toNormalizedString(text) : "";
System.out.println("Found match at ID " + id + " in field name '" + elementName + "' with value '" + txt + "'");
count++;
}
System.out.println("Total number of matches: " + count);
assertThat(count, is(equalTo(2)));
}
这不起作用,因为current()函数是XSLT独有的。因此,我尝试声明一个名为“current”to“.”的变量表达式来表示“thecurrentbook”,但这也不起作用,因为(顾名思义),变量表达式不存储表达式的结果,而是存储表达式本身
在VTD-XML中,是否有一种方法可以仅使用XPath表达式来实现这种效果?
(我意识到在代码中有各种各样的方法,但我想使用纯XPath,这样用户就可以轻松地创建一个描述其数据格式的配置文件)
编辑:
接受答案的结果是,我想要的不能使用单个XPath表达式。最后,我添加了一个选项,这样用户就可以指定如何找到当前图书的唯一标识符(即“/@id”或“/isbn”)。然后,我的代码执行此表达式并用结果替换评级搜索XPath中的某些占位符(例如“$$”)。类似于
/*/rating[./@book id=///book/@id]/@value的XPath表达式应仅检索与可用图书id匹配的评级值
如果将
添加到XML文档中,XPath将只返回Book1和Book2的值5
和3
,因为没有ID为3的书籍可用
VTD的简单测试方法如下所示:
<root>
<book id="1" name="Book1"/>
<book id="2" name="Book1"/>
<rating book-id="1" value="5"/>
<rating book-id="2" value="3"/>
</root>
@Test
public void xpathReference() throws Exception {
byte[] bytes = ("<root>\n"
+ " <book id=\"1\" name=\"Book1\"/>\n"
+ " <book id=\"2\" name=\"Book1\"/>\n"
+ " <rating book-id=\"1\" value=\"5\"/>\n"
+ " <rating book-id=\"2\" value=\"3\"/>\n"
+ " <rating book-id=\"3\" value=\"4\"/>\n"
+ "</root>").getBytes();
VTDGen vtdGenerator = new VTDGen();
vtdGenerator.setDoc(bytes);
vtdGenerator.parse(true);
VTDNav vtdNavigator = vtdGenerator.getNav();
AutoPilot autoPilot = new AutoPilot(vtdNavigator);
autoPilot.selectXPath("//*/rating[./@book-id=//book/@id]/@value");
int id;
int count = 0;
while ((id = autoPilot.evalXPath()) != -1) {
String elementName = vtdNavigator.toString(id);
int text = vtdNavigator.getAttrVal(elementName);
String txt = text != -1 ? vtdNavigator.toNormalizedString(text) : "";
System.out.println("Found match at ID " + id + " in field name '" + elementName + "' with value '" + txt + "'");
count++;
}
System.out.println("Total number of matches: " + count);
assertThat(count, is(equalTo(2)));
}
根据注释,上面的代码没有以类似于迭代的方式提取当前处理的书籍的数据。下面的代码现在尝试实现这一点:
@Test
public void xpathReference() throws Exception {
byte[] bytes = ("<root>\n"
+ " <book id=\"1\" name=\"Book1\"/>\n"
+ " <book id=\"2\" name=\"Book2\"/>\n"
+ " <book id=\"4\" name=\"Book3\"/>\n"
+ " <rating book-id=\"1\" value=\"5\"/>\n"
+ " <rating book-id=\"2\" value=\"3\"/>\n"
+ " <rating book-id=\"3\" value=\"4\"/>\n"
+ "</root>").getBytes();
VTDGen vtdGenerator = new VTDGen();
vtdGenerator.setDoc(bytes);
vtdGenerator.parse(true);
VTDNav vtdNavigator = vtdGenerator.getNav();
AutoPilot autoPilot = new AutoPilot(vtdNavigator);
autoPilot.selectXPath("//book/@id");
int id;
int count = 0;
while ((id = autoPilot.evalXPath()) != -1) {
String elementName = vtdNavigator.toString(id);
int bookId_id = vtdNavigator.getAttrVal(elementName);
String bookId = bookId_id != -1 ? vtdNavigator.toNormalizedString(bookId_id) : "";
AutoPilot xpathBookName = new AutoPilot(vtdNavigator);
xpathBookName.selectXPath("//book[@id=" + bookId + "]/@name");
String bookName = xpathBookName.evalXPathToString();
AutoPilot xpathRating = new AutoPilot(vtdNavigator);
xpathRating.selectXPath("//rating[@book-id=" + bookId + "]/@value");
String bookRating = xpathRating.evalXPathToString();
if ("".equals(bookRating)) {
System.out.println("Book " + bookName + " with id " + bookId + " has no rating yet");
} else {
System.out.println("Book " + bookName + " with id " + bookId + " has a rating of " + bookRating);
}
count++;
}
System.out.println("Total number of matches: " + count);
assertThat(count, is(equalTo(3)));
}
请注意,我确实稍微更新了您第二本书的名称,以便您可以更容易地看到差异
。。。是的,用Java代码获取当前书籍的id,然后用它构造一个XPath表达式是很容易的,但是正如我所解释的,我希望用户能够使用XPath定义他们的文档格式,所以我不希望代码中有任何特定于格式的内容
VTD只支持XPath 1.0。如果您(或您的客户机)能够提出XPath1.0查询,那么您也应该能够通过VTD提取相应的值。我想,普通XPath查询的表达能力不足以直接交付所需内容
由于该示例对于您所需的用例来说可能过于简单,因此很难就如何设计应用程序来处理此类场景给出任何建议。也许可以用更详细的例子更新你的问题。处理此问题的一个简单方法是引入必须单独定义的占位符变量,然后在尝试执行此类XPath表达式时点击此类占位符,只需将这些占位符替换为以前提取的值的具体值。谢谢您的回答,但是我想你忽略了我在反复阅读书籍,并且只使用XPath来获得当前书籍的评级这一事实。(是的,用Java代码获取当前书籍的id,然后用它构造一个XPath表达式是很容易的,但正如我所解释的,我希望用户能够使用XPath定义他们的文档格式,所以我不希望代码中有任何特定于格式的内容)@zwets更新帖子以使用当前处理的书籍,以便提取名称和评级银行!我采用了类似于您在帖子中建议的方法,基本上实现了我自己的变量系统。顺便说一句,只是一个建议,但是你可以在你的答案的顶部加一个简短的注释,归结为“不,这不可能,但是请看下面的建议”。不,我的问题已经回答了。明天我将发送一个后续消息,并附上这篇文章的链接。顺便说一下,感谢VTD-XML,它太棒了!
Book Book1 with id 1 has a rating of 5
Book Book2 with id 2 has a rating of 3
Book Book3 with id 4 has no rating yet
Total number of matches: 2