Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

xml - find common parent using Xpath

I was wondering if there was any way to access common parent node using Xpath.

<outer>
<main>
           <a><b> sometext </b></a>
           <c><d> sometext2 </d></c>
</main>
</outer>

I have the text nodes sometext and sometext2. is there a way i can access main (common parent) of these two nodes? I do not know the layout of the xml containing these nodes.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Use the following XPath 1.0 expression:

$v1/ancestor::*
   [count(. | $v2/ancestor::*) 
   = 
    count($v2/ancestor::*)
   ]
    [1]

where $v1 and $v2 hold the two text nodes (in case you use XPath not within XSLT, you will have to replace $v1 and $v2 in the above expression with the XPath expressions that select each one of these two text nodes).

Explanation:

The above XPath 1.0 expression finds the intersection of two node-sets: the node-set of all element ancestors of $v1 and the node-set of all element ancestors of $v2. This is done with the so called Kaysian method for intersection (after Michael Kay, who discovered this in 2000). Using the Kaysian method for intersection, the intersection of two nodesets, $ns1 and $ns2 is selected by the following XPath expression:

  $ns1[count(. | $ns2) = count($ns2)]

Then, from the intersection of the ancestors we must select the last element. However, because we are using a reverse axis (ancestor), the required node position must be denoted as 1.

One can quickly verify that the above XPath expression really selects the lowest common ancestor, by applying the following transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output method="text"/>

  <xsl:variable name="v1" select="/*/*/a/b/text()"/>
  <xsl:variable name="v2" select="/*/*/c/d/text()"/>

  <xsl:variable name="vCommonAncestor" select=
   "$v1/ancestor::*
       [count(. | $v2/ancestor::*) 
       = 
        count($v2/ancestor::*)
       ]
        [1]"
   />

    <xsl:template match="/">
      <xsl:value-of select="name($vCommonAncestor)"/>
    </xsl:template>
</xsl:stylesheet>

when applied on the originally-provided XML document (corrected to well-formed XML):

<outer>
    <main>
        <a>
            <b>sometext</b>
        </a>
        <c>
            <d>sometext2</d>
        </c>
    </main>
</outer>

the wanted result (the name of the element that is the lowest common ancestor of the two text nodes) is produced:

main

The XPath 2.0 expression that selects the lowest common ancestor of the two nodes is simpler, because it uses the standard XPath 2.0 operator "intersect":

   ($v1/ancestor::* intersect $v2/ancestor::*) 
                                         [last()]

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...