我有一个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引擎运行时会抛出错误。是否有可用于选择元素的匹配表达式?
答案 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)]"
。