Xpath查询根据元素值获取祖先节点

时间:2012-02-14 02:43:40

标签: xml xslt xpath xml-parsing

我试图找到遵循以下两条规则的所有元素名称。

1
。元素应该有<set>erase</set>

2。如果两个或多个元素在层次结构中具有<set>erase</set>(例如:<b><d>都具有<set>erase</set>),那么只需要打印父节点名称(即{{ 1}}在这种情况下)。

因此,xml以下所需的结果必须是:

  

b,y,p

<b>

当我使用<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <a> <b> <set>erase</set> <d> <set>erase</set> </d> </b> <c> <x> </x> </c> <e> <y> <set>erase</set> <q> </q> </y> <z> <p> <set>erase</set> </p> </z> </e> </a> 时,我只在结果集中获得节点query = (//set[contains(.,'erase')])[1] 当我使用b时,我在结果集中获取所有nodesList query = //set[contains(.,'erase')]

任何人都可以帮我找到导致nodeList b,d,y,pby的查询。

这是我使用的java代码片段。

p

输出:

        XPath xpath = factory.newXPath();
    String query = "//set[contains(.,'erase')]";
            XPathExpression expr=null;
    try {
        expr = xpath.compile(query);
    } catch (XPathExpressionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
        Object result = null;
    try {
        result = expr.evaluate(doc, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
        e.printStackTrace();
    }
    NodeList nodes = (NodeList) result;


    for (int i = 0; i < nodes.getLength(); i++) {
        String x = "";
        Node n = nodes.item(i).getParentNode();
        x=n.getNodeName();
        while(!n.getNodeName().equalsIgnoreCase(request.getClass().getSimpleName())){
            if ((n = n.getParentNode())!=null){
                x=n.getNodeName()+"."+x;
            }
        }



        System.out.println("Path: "+x);

任何人都可以帮我找出仅导致a.b a.b.d a.e.y a.e.z.p 的查询 如果您需要更多详细信息,请告诉我们。或任何其他用例。

4 个答案:

答案 0 :(得分:1)

一个完全选择所需元素的表达式是

      //*[set[. = 'erase' and not(node()[2])]
         and
          not(ancestor::*
                 [set
                    [. = 'erase' and not(node()[2])]
                 ]
              )
          ]

基于XSLT的验证

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <xsl:template match="/">
         <xsl:for-each select=
         "//*[set[. = 'erase' and not(node()[2])]
             and
              not(ancestor::*
                     [set
                        [. = 'erase' and not(node()[2])]
                     ]
                  )
              ]">

          <xsl:value-of select="name()"/>
          <xsl:text>&#xA;</xsl:text>
        </xsl:for-each>
     </xsl:template>
</xsl:stylesheet>

此转换适用于Sean B. Durkin XML文档提供的

<a>
    <b>
        <set>erase</set>
        <set>
            <a/>erase
        </set>
        <d>
            <set>erase</set>
        </d>
    </b>
    <c>
        <x>         </x>
    </c>
    <e>
        <y>
            <set>erase</set>
            <q>             </q>
        </y>
        <z>
            <p>
                <set>erase</set>
            </p>
        </z>
    </e>
</a>

评估上面的XPath表达式并输出所选元素的名称 - 生成所需的正确结果

b
y
p

请注意以下两个表达式非常不正确:

*[set[text()='erase']][not(ancestor::*[set[text()='erase']])]  

或者:

*[set[text()='erase']][ancestor::*[set[text()!='erase']]] 

这两个表达式存在多个问题

  1. 它们是相对表达式,无论它们应用于哪个初始上下文,它们都无法选择层次结构中具有未定义深度和结构的所有想要元素。

  2. set[text()='erase']不仅会选择表单元素:

  3. ...

    <set>erase</set>
    

    还有表格的元素:

    <set>
    xyz
     <a/>erase</set>   
    

    0.3。类似地:

    set[text()!='erase']   
    

    选择表单元素:

    <set>
    xyz
     <a/>erase</set> 
    

答案 1 :(得分:0)

这是我的第二次尝试:

//*[                    set[count(node())=1 and text()='erase'] and
      not( ancestor::*[ set[count(node())=1 and text()='erase']])
   ]

此选择通过了我第一个答案中显示的测试用例。

答案 2 :(得分:-1)

以下XPath选择所需的节点:

//*[set[text()='erase']][not(ancestor::*[set[text()='erase']])]

我使用以下样式表进行了测试

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" encoding="utf-8" indent="yes"/>

    <xsl:template match="@*|text()" />

    <xsl:template match="//*[set[text()='erase']][not(ancestor::*[set[text()='erase']])]">
        <xsl:text>(</xsl:text>
        <xsl:for-each select="self::*|ancestor::*">
            <xsl:value-of select="name()"/>
            <xsl:text>.</xsl:text>
        </xsl:for-each>
        <xsl:text>) </xsl:text>
    </xsl:template>

</xsl:stylesheet>

它产生了输出

(a.b.) (a.e.y.) (a.e.z.p.)

答案 3 :(得分:-1)

或者这个关于Harpo答案的轻微调整?:

*[set[text()='erase']][ancestor::*[set[text()!='erase']]]

根据我对Novatchev答案的评论,请考虑有用的测试用例:

这是对问讯者演示文档的更改。我添加了另一个节点。

<?xml version="1.0"?>
<a>
    <b>
        <set>erase</set>
        <set><a/>erase</set>
        <d>
        <set>erase</set>
        </d>
    </b>
    <c>
        <x>
        </x>
    </c>
    <e>
        <y>
        <set>erase</set>
            <q>
            </q>
        </y>
        <z>
            <p>
            <set>erase</set>
            </p>
        </z>
    </e>
</a>

答案应该是

b
y
p