寻找嵌套的XML标签的索引

huangapple go评论72阅读模式
英文:

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 &lt;Nested&gt; tag in ur code, Look at the below code

public static void main(String[] args) throws TransformerException, SAXException, IOException, ParserConfigurationException {
	String xmlString = &quot;&lt;body&gt;\r\n&quot; + 
			&quot;    &lt;nested attr=\&quot;bla\&quot;&gt;\r\n&quot; + 
			&quot;        &lt;name&gt;foo&lt;/name&gt;\r\n&quot; + 
			&quot;    &lt;/nested&gt;\r\n&quot; + 
			&quot;    &lt;nested attr=\&quot;blub\&quot;&gt;\r\n&quot; + 
			&quot;        &lt;name&gt;bar&lt;/name&gt;\r\n&quot; + 
			&quot;    &lt;/nested&gt;\r\n&quot; + 
			&quot;    &lt;nested attr=\&quot;bli\&quot;&gt;\r\n&quot; + 
			&quot;        &lt;name&gt;baz&lt;/name&gt;\r\n&quot; + 
			&quot;    &lt;/nested&gt;\r\n&quot; + 
			&quot;&lt;/body&gt;&quot;;

	CachedXPathAPI cachedXPathAPI = new CachedXPathAPI();
	DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
	Document document = builder.parse(new InputSource(new StringReader(xmlString)));	
	NodeList list = cachedXPathAPI.selectNodeList(document, &quot;/body/nested&quot;);

	for(int i=0; i&lt;list.getLength(); i++ ) {
		String value = list.item(i).getAttributes().getNamedItem(&quot;attr&quot;).getTextContent();
		System.out.println(list.item(i).getNodeName()+&quot; @Index &quot;+i+&quot; attr:: &quot;+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>

在这里,没有任何元素具有字符串值为 barname 子元素。当我们应用转换:

<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=&#39;bar&#39;]/preceding-sibling::*) + 1

XSLT-based verification:

The following transformation simply evaluates the XPath expression and outputs the result of this evaluation:

&lt;xsl:stylesheet version=&quot;1.0&quot; xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;&gt;
 &lt;xsl:output omit-xml-declaration=&quot;yes&quot; indent=&quot;yes&quot;/&gt;

  &lt;xsl:template match=&quot;/&quot;&gt;
    &lt;xsl:value-of select=&quot;count(/*/*[name=&#39;bar&#39;]/preceding-sibling::*) +1&quot;/&gt;
  &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;

When applied on the provided XML document:

&lt;body&gt;
    &lt;nested attr=&quot;bla&quot;&gt;
        &lt;name&gt;foo&lt;/name&gt;
    &lt;/nested&gt;
    &lt;nested attr=&quot;blub&quot;&gt;
        &lt;name&gt;bar&lt;/name&gt;
    &lt;/nested&gt;
    &lt;nested attr=&quot;bli&quot;&gt;
        &lt;name&gt;baz&lt;/name&gt;
    &lt;/nested&gt;
&lt;/body&gt;

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=&#39;bar&#39;])) * (count(/*/*[name=&#39;bar&#39;]/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:

&lt;body&gt;
    &lt;nested attr=&quot;bla&quot;&gt;
        &lt;name&gt;foo&lt;/name&gt;
    &lt;/nested&gt;
    &lt;nested attr=&quot;blub&quot;&gt;
        &lt;name&gt;barr&lt;/name&gt;
    &lt;/nested&gt;
    &lt;nested attr=&quot;bli&quot;&gt;
        &lt;name&gt;baz&lt;/name&gt;
    &lt;/nested&gt;
&lt;/body&gt;

Here no element has a &lt;name&gt; child with string value &#39;bar&#39;. And when we apply the transformation:

&lt;xsl:stylesheet version=&quot;1.0&quot; xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;&gt;
 &lt;xsl:output omit-xml-declaration=&quot;yes&quot; indent=&quot;yes&quot;/&gt;

  &lt;xsl:template match=&quot;/&quot;&gt;
    &lt;xsl:value-of select=
    &quot;(boolean(/*/*[name=&#39;bar&#39;])) * (count(/*/*[name=&#39;bar&#39;]/preceding-sibling::*) +1)&quot;/&gt;
  &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;

the correct, wanted result is produced:

0

huangapple
  • 本文由 发表于 2020年9月24日 15:00:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/64041086.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定