英文:
Find index of nested XML tag
问题
假设我有一个像这样的XML:
<body>
<nested attr="bla">
<name>foo</name>
</nested>
<nested attr="blub">
<name>bar</name>
</nested>
<nested attr="bli">
<name>baz</name>
</nested>
</body>
现在我想要的是一种通过特定值获取`nested`标签索引的方法。例如,`bar`的索引将是1(或者如果从1开始计数则为2)。
我已经使用了Apache CachedXPathApi完成了这个任务:
public int getIndex(String path, String value) throws TransformerException {
NodeIterator it = cachedXPathAPI.selectNodeIterator(document, path);
Node node;
int i = 0;
while((node = it.nextNode()) != null) {
if(node.getTextContent().equals(value)) {
return i;
}
++i;
}
return -1;
}
这对于从属性`attr`获取索引很有效,就像这样:
```getIndex("/body/nested/@attr", "blub")```
但我不知道如何为嵌套的值执行此操作。如果我使用```/body/nested/name```,那么它显然只会计算`nested`中的`name`标签,这不是我想要的。
我该如何解决这个问题,无论是通过更改Java代码还是甚至使用特殊的XPath表达式?
英文:
suppose I have an XML like this:
<body>
<nested attr="bla">
<name>foo</name>
</nested>
<nested attr="blub">
<name>bar</name>
</nested>
<nested attr="bli">
<name>baz</name>
</nested>
</body>
What I want now is a way to get the index of the nested
tag with a certain value. So for example the index of bar
would be 1 (or 2 if you count from 1).
I have already done this using Apache CachedXPathApi:
public int getIndex(String path, String value) throws TransformerException {
NodeIterator it = cachedXPathAPI.selectNodeIterator(document, path);
Node node;
int i = 0;
while((node = it.nextNode()) != null) {
if(node.getTextContent().equals(value)) {
return i;
}
++i;
}
return -1;
}
Which works fine for getting the index from the attribute attr
like this:
getIndex("/body/nested/@attr", "blub")
But I don't know how to do this for the nested values. If I use /body/nested/name
then it will obviously only ever count the name
tags within nested
which is not what I want.
How can I solve this, either by changing the Java Code or maybe even with a special XPath expression?
答案1
得分: 1
如果我正确理解您的问题,您想要在您的代码中获取 <Nested>
标签的索引。请查看以下代码:
public static void main(String[] args) throws TransformerException, SAXException, IOException, ParserConfigurationException {
String xmlString = "<body>\r\n" +
" <nested attr=\"bla\">\r\n" +
" <name>foo</name>\r\n" +
" </nested>\r\n" +
" <nested attr=\"blub\">\r\n" +
" <name>bar</name>\r\n" +
" </nested>\r\n" +
" <nested attr=\"bli\">\r\n" +
" <name>baz</name>\r\n" +
" </nested>\r\n" +
"</body>";
CachedXPathAPI cachedXPathAPI = new CachedXPathAPI();
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(xmlString)));
NodeList list = cachedXPathAPI.selectNodeList(document, "/body/nested");
for(int i=0; i<list.getLength(); i++ ) {
String value = list.item(i).getAttributes().getNamedItem("attr").getTextContent();
System.out.println(list.item(i).getNodeName()+" @Index "+i+" attr:: "+value);
}
}
result
nested @Index 0 attr:: bla
nested @Index 1 attr:: blub
nested @Index 2 attr:: bli
英文:
If I understood ur question correctly U want to have the index of the <Nested>
tag in ur code, Look at the below code
public static void main(String[] args) throws TransformerException, SAXException, IOException, ParserConfigurationException {
String xmlString = "<body>\r\n" +
" <nested attr=\"bla\">\r\n" +
" <name>foo</name>\r\n" +
" </nested>\r\n" +
" <nested attr=\"blub\">\r\n" +
" <name>bar</name>\r\n" +
" </nested>\r\n" +
" <nested attr=\"bli\">\r\n" +
" <name>baz</name>\r\n" +
" </nested>\r\n" +
"</body>";
CachedXPathAPI cachedXPathAPI = new CachedXPathAPI();
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(xmlString)));
NodeList list = cachedXPathAPI.selectNodeList(document, "/body/nested");
for(int i=0; i<list.getLength(); i++ ) {
String value = list.item(i).getAttributes().getNamedItem("attr").getTextContent();
System.out.println(list.item(i).getNodeName()+" @Index "+i+" attr:: "+value);
}
}
result
nested @Index 0 attr:: bla
nested @Index 1 attr:: blub
nested @Index 2 attr:: bli
答案2
得分: 1
只评估这个XPath表达式:
count(/*/*[name='bar']/preceding-sibling::*) + 1
基于XSLT的验证:
以下转换简单地评估XPath表达式,并输出此评估的结果:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="count(/*/*[name='bar']/preceding-sibling::*) + 1"/>
</xsl:template>
</xsl:stylesheet>
当应用于提供的XML文档时:
<body>
<nested attr="bla">
<name>foo</name>
</nested>
<nested attr="blub">
<name>bar</name>
</nested>
<nested attr="bli">
<name>baz</name>
</nested>
</body>
会产生期望的正确结果:
2
II. 更新
在评论中,楼主描述了另一种情况,即XML文档中没有满足筛选条件的元素。在这种情况下,上面的表达式返回1
,这是不正确的。
以下是一个在所有情况下都返回正确结果的XPath表达式:
(boolean(/*/*[name='bar'])) * (count(/*/*[name='bar']/preceding-sibling::*) + 1)
这是将前一个XPath表达式(上面的)乘以另一个XPath表达式(在其左侧),该表达式在没有满足筛选谓词的元素时计算为0
,否则为1
。
基于XSLT的验证 表明,当存在满足条件的元素时,此XPath表达式评估为正确的基于1的索引值;当不存在这样的元素时,评估为0
。我们利用了隐式转换 number(false())
为 0
,以及 number(true())
为 1
的事实。
以下是后一个示例:
<body>
<nested attr="bla">
<name>foo</name>
</nested>
<nested attr="blub">
<name>barr</name>
</nested>
<nested attr="bli">
<name>baz</name>
</nested>
</body>
在这里,没有任何元素具有字符串值为 bar
的 name
子元素。当我们应用转换:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="(boolean(/*/*[name='bar'])) * (count(/*/*[name='bar']/preceding-sibling::*) + 1)"/>
</xsl:template>
</xsl:stylesheet>
将产生正确的期望结果:
0
英文:
Just evaluate this XPath expression:
count(/*/*[name='bar']/preceding-sibling::*) + 1
XSLT-based verification:
The following transformation simply evaluates the XPath expression and outputs the result of this evaluation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="count(/*/*[name='bar']/preceding-sibling::*) +1"/>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<body>
<nested attr="bla">
<name>foo</name>
</nested>
<nested attr="blub">
<name>bar</name>
</nested>
<nested attr="bli">
<name>baz</name>
</nested>
</body>
the wanted, correct result is produced:
2
II. Update
In a comment the OP described another case, where there is no element in the XML document that satisfies the filtering condition. In such case the previous expression (above) returns 1
and this is incorrect.
Here is an XPath expression that returns the correct result in all cases:
(boolean(/*/*[name='bar'])) * (count(/*/*[name='bar']/preceding-sibling::*) +1)
This is the previous XPath expression, multiplied by another XPath expression (on its left) which evaluates to 0
if there is no element that satisfies the filtering predicate, and to 1
otherwise.
The XSLT-based verification shows that this XPath expression evaluates to the correct 1-based index values in the case when a satisfying element exists, and to 0
when no such element exists. Here we take advantage of the fact that the implicit conversion number(false())
is 0
and number(true())
is 1
.
Here is the latter example:
<body>
<nested attr="bla">
<name>foo</name>
</nested>
<nested attr="blub">
<name>barr</name>
</nested>
<nested attr="bli">
<name>baz</name>
</nested>
</body>
Here no element has a <name>
child with string value 'bar'
. And when we apply the transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"(boolean(/*/*[name='bar'])) * (count(/*/*[name='bar']/preceding-sibling::*) +1)"/>
</xsl:template>
</xsl:stylesheet>
the correct, wanted result is produced:
0
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论