与XML文档中特定元素的第一个匹配项匹配的模板

时间:2014-01-27 16:11:38

标签: xml xslt xpath

我有一个XML文档,其结构类似于以下内容:

<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>

真正的XML当然可以比上面复杂得多,但它应该足以说明。

我只需要将特定模板应用于<p>元素内的第一个<body>元素。我可以轻松编写一个xpath表达式,为我感兴趣的节点选择:

(//body//p)[1]

不幸的是,这个xpath表达式不能用作XSLT中的匹配表达式

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

上面的模板在尝试通过XSL引擎运行时会抛出错误。是否有可用于选择元素的匹配表达式?

2 个答案:

答案 0 :(得分:3)

你必须使用像

这样的东西
<xsl:template match="body//p[count(preceding::p) = count(ancestor::body/preceding::p)]">

即。在正文中找到p it 之前的其他p元素的数量与开始<body>标记之前的数字相同。

可选地

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

会查找没有任何前导p元素的p,这些元素本身就是body的后代。 ancestor::body约束是必要的,因为每个 body//p前面都有p内的abstract。如果可能有多个body元素,它会变得更复杂,因为我们只检查 p元素中的body ,不一定相同的 body

答案 1 :(得分:1)

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