首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对属性(按值)使用一个模板,对(父)节点使用另一个模板

对属性(按值)使用一个模板,对(父)节点使用另一个模板
EN

Stack Overflow用户
提问于 2014-08-18 14:55:46
回答 2查看 432关注 0票数 2

对于大型(TEI风味的) XML文件(~6000行)(使用OS上的Saxon 9.5.1.6J )的较大(420行) XSL转换,我得到了这个模糊的匹配警告。我想理解(并修正)这个警告。

代码语言:javascript
复制
 Recoverable error 
  XTRE0540: Ambiguous rule match for /TEI/text[1]/group[1]/text[1]/body[1]/lg[33]/head[2]
 Matches both "tei:lg[@type='poem']/tei:head" on line 103 of
  file: hs2latex.xsl
 and "*[@rend='italics']" on line 110 of
  file: hs2latex.xsl

XML看起来类似于:

代码语言:javascript
复制
<lg type='poem'>
<head rend='italics'>Sonnet 3<head>
...
</lg>

对于冲突的XSL规则,如下所示:

代码语言:javascript
复制
<xsl:template match="tei:lg[@type='poem']/tei:head">
...
<xsl:apply-templates />
</xsl:template>

代码语言:javascript
复制
<xsl:template match="*[@rend='italics']"><!-- blah blah --></xsl:template>

因为属性只是另一个节点,所以我想我可以单独匹配它。但是,如果我在匹配中只有一个属性,我就会得到一个错误,因此我将使用asterisk来匹配所有节点,并使用rend=‘斜体’属性,这就产生了上面引用的不明确的错误。

是否有可能完成我在这里尝试的工作,即使用一个模板来匹配基于值的属性(不管元素的类型如何)?我感兴趣的是拥有一个带有“@rend=‘斜体’”属性的任何元素的模板。

我试着用一个最小的工作例子来重现这个问题,但是我想出了一个稍微不同的例子(这可能就是我误解的地方)。

最小工作XML

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>

<document>
  <book>
    <title>"One Title"</title>
  </book>

  <book>
    <title rend="italics">Another Title</title>
  </book>
</document>

最小XSLT

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        exclude-result-prefixes="xsl">
  <xsl:output omit-xml-declaration="yes" />
  <xsl:template match="/">
    <xsl:apply-templates />
  </xsl:template>

  <xsl:template match="*[@rend='italics']">
    <italics><xsl:apply-templates /></italics>
  </xsl:template>

  <xsl:template match="title">
    <title><xsl:apply-templates /></title>
  </xsl:template>

</xsl:stylesheet>

这个最小的示例(我认为它产生的情况与我前面描述的情况相同)并不会产生一个模糊的匹配错误,而是产生这个输出:

代码语言:javascript
复制
<title>"One Title"</title>
<italics>Another Title</italics>

我想要的(在这个最小的例子中)是:

代码语言:javascript
复制
<title>"One Title"</title>
<title><italics>Another Title</italics></title>

我怀疑我误解了XSLT或XPath的一些基本内容,但此时我感到不知所措,希望得到任何指导。非常感谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-08-18 15:18:43

是否有可能完成我在这里尝试的工作,即使用一个模板来匹配基于值的属性(不管元素的类型如何)?我感兴趣的是拥有一个带有“@rend=‘斜体’”属性的任何元素的模板。

是的,这是可能的。代码中的问题是,您有两个表单NodeTest[predicate]的匹配模板,它们在默认情况下都具有相同的优先级。如果您希望一个优先于另一个,您应该添加一个priority="X",其中X是任意数字。即:

代码语言:javascript
复制
<xsl:template match="*[@rend='italics']" priority="2">
  <italics><xsl:apply-templates /></italics>
</xsl:template>

这个最小的示例(我认为它产生的情况与我前面描述的情况相同)并不会产生一个模糊的匹配错误,而是产生这个输出:

对,是这样。这是因为在XSLT中,默认值根据匹配模式的复杂性分配给优先级。。只是NodeTest的优先级比NodeTest[predicate]低。

因为属性只是另一个节点,所以我想我可以单独匹配它。

可以,停那儿吧。您没有展示您尝试过的属性匹配,但是它应该如下所示:match="@rend"match="@rend[. = 'italics']"。但是,请注意属性是特殊的节点。您需要具体地将模板应用于属性,以便能够匹配它们。此外,具有焦点的节点将是属性节点本身,因此您可能必须遍历父轴才能获得与当前相同的结果。

我想要的(在这个最小的例子中)是:

您似乎想要的是,当更一般的匹配匹配时,您希望将特定的匹配也应用到同一个节点。任何节点最多匹配一个匹配模板。要使一个节点匹配多个模板,可以使用xsl:next-match指令。但是,这可以从特定的(首先匹配)到泛型(最后匹配)。

在你的情况下,你想要的是相反的。我会这样做,它提供了您期望的输出(由于显式优先级,所有title -元素都首先匹配标题模板,并且只有斜体元素也匹配斜体模板,将<italics>添加到输出中):

代码语言:javascript
复制
<xsl:template match="/">
    <xsl:apply-templates />
</xsl:template>

<xsl:template match="*[@rend='italics']">
    <italics><xsl:apply-templates /></italics>
</xsl:template>

<xsl:template match="title" priority="2">
    <title><xsl:next-match /></title>
</xsl:template>

您可能希望在更大的示例中应用类似的编码模式,否则,<italics>是唯一匹配的,我认为您也希望两者都匹配,并且顺序正确(首先是泛型的,然后是特定的)。

票数 3
EN

Stack Overflow用户

发布于 2014-08-18 22:22:22

亚伯已经给出了一个很好的答案,我写另一个答案的原因是:

代码语言:javascript
复制
...but if I understand you, it is my only real option.

远非如此--就像编程中经常出现的情况一样,有不止一种方法可以做到这一点。我不会说xsl:next-match很难看,但它破坏了许多样式表中存在的一个原则,即每个节点只处理一次,并为其找到一个合适的模板。

另一种选择是匹配应该转换为将文本内容封装在单独模板中的元素的属性:

代码语言:javascript
复制
<xsl:template match="title/@*">
  <xsl:element name="{.}">
     <xsl:apply-templates select="../text()"/>
  </xsl:element>
</xsl:template>

如果我理解正确的话,这就是你一开始就想要做的。上面的模板匹配title元素的任何属性。然后,构造一个新元素,其名称对应于属性值(在本例中为“斜体”)。最后,将文本节点的内置模板应用于处理属性父节点的文本内容。

样式表

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.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="/document">
    <xsl:copy>
        <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="title/@*">
    <xsl:element name="{.}">
        <xsl:apply-templates select="../text()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="title">
    <title>
        <xsl:choose>
            <xsl:when test="@*">
                <xsl:apply-templates select="@*"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates/>
            </xsl:otherwise>
        </xsl:choose>
    </title>
  </xsl:template>

</xsl:stylesheet>

顺便提一下,您不需要使用exclude-result-prefixes="xsl"从结果树中排除XSLT命名空间。任何以xsl:为前缀的内容都被识别为要执行的指令,并且名称空间在默认情况下被排除在结果的XML中。

XML输出

代码语言:javascript
复制
<document>
   <title>"One Title"</title>
   <title>
      <italics>Another Title</italics>
   </title>
</document>
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25366268

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档