简体   繁体   中英

Template that matches the first occurrence of a specific element in an XML document

I have an XML document with a structure similar to the following:

<article>
   <header>
      <metadata>
          bunch of metadata nodes here
          <abstract>
              <p>this is one of the abstract's paragraphs</p>
          </abstract>
      </metadata>
   </header>
   <body>
      <sec>
         <title>This is title 1</title>
         <p>Paragraph 1</p>
         <p>paragraph 2</p>
         <title>This is title 2</title>
      <sec>
         <title>This is title 3</title>
         <p>paragraph 1 under title 3</p>
         <p>paragraph 2 under title 3</p>
   </body>
 </article>

The real XML can certainly get a lot more complex that the above, but it should suffice to illustrate.

I need to apply a specific template only to the first <p> element occurring inside the <body> element. I can easily write an xpath expresion that selects for the the node I am interested in:

(//body//p)[1]

Unfortunately this xpath expression can not be used as the match expression in an XSLT

<xsl:template match="(//body//p)[1]">
<p_first>
    <xsl:call-template name="copyElementAttributes" />
    <xsl:apply-templates select="@*|node()" />
</p_first>
</xsl:template>

The template above will throw an error when trying to run it through the XSL engine. Is there a match expression that I can use to select the element?

You'd have to use something like

<xsl:template match="body//p[count(preceding::p) = count(ancestor::body/preceding::p)]">

ie to find a p inside the body for which the number of other p elements ahead of it is the same as the number ahead of the opening <body> tag.

Alternatively

<xsl:template match="body//p[not(preceding::p[ancestor::body])]">

would look for a p that does not have any predecessor p elements that are themselves descendants of the body . The ancestor::body constraint is necessary because every body//p is preceded by the p that is inside the abstract . It would get more complicated still if there could be more than one body element, since we're only checking for p s that are inside a body element, not necessarily the same body .

尝试match="body//p[not(preceding::p)]"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM