2

我在大型(TEI 风格)XML 文件(约 6000 行)(在 OS X 上使用 Saxon-HE 9.5.1.6J)的较大(420 行)XSL 转换上收到此模棱两可的匹配警告。我想了解(并修复)警告。

 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 看起来像:

<lg type='poem'>
<head rend='italics'>Sonnet 3<head>
...
</lg>

冲突的 XSL 规则看起来像这样:

<xsl:template match="tei:lg[@type='poem']/tei:head">
...
<xsl:apply-templates />
</xsl:template>

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

由于属性只是另一个节点,我想我可以单独匹配它。但是,如果我的匹配项中只有一个属性,则会出现错误,因此我将 used 加到星号以匹配具有 rend='italics' 属性的所有节点,这会产生上面引用的模棱两可的错误。

是否可以做我在这里尝试的事情,即使用一个模板来匹配基于值的属性(无论元素的类型如何)?我有兴趣让一个模板处理任何具有例如“@rend='italics'”属性的元素。

我试图用一个最小的工作示例来重现这个问题,但提出了一个稍微不同的示例(这可能正是我所误解的)。

最小的工作 XML

<?xml version="1.0" encoding="utf-8"?>

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

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

最小 XSLT

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

这个最小的示例(我认为它会产生与我上面描述的相同的情况)不会产生模棱两可的匹配错误,而是会导致以下输出:

<title>"One Title"</title>
<italics>Another Title</italics>

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

<title>"One Title"</title>
<title><italics>Another Title</italics></title>

我怀疑我误解了有关 XSLT 或 XPath 的一些基本知识,但我现在不知所措,希望得到任何指导。非常感谢。

4

2 回答 2

3

是否可以做我在这里尝试的事情,即使用一个模板来匹配基于值的属性(无论元素的类型如何)?我有兴趣让一个模板处理任何具有例如“@rend='italics'”属性的元素。

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

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

这个最小示例(我认为它会产生与我上面描述的相同的情况)不会产生模棱两可的匹配错误,而是会导致以下输出:

正确的。这是因为在 XSLT 中,默认值是根据匹配模式的复杂性大致分配给优先级的。只是 a 的NodeTest优先级低于 a NodeTest[predicate]

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

是的你可以。您没有显示您对属性匹配的尝试,但它应该看起来像这样:match="@rend"match="@rend[. = 'italics']". 但是,请注意属性是特殊节点。您需要专门将模板应用于属性才能匹配它们。此外,具有焦点的节点将是属性节点本身,因此您可能必须遍历父轴才能获得与当前相同的结果。

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

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

在你的情况下,你想要相反。我会做这样的事情,它会给出你期望的输出(所有标题元素首先匹配标题模板,因为明确的优先级,只有斜体元素也匹配斜体模板,添加<italics>到输出):

<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>是唯一匹配的,我认为您也希望两者都匹配,并且以正确的顺序(首先是通用的,然后是特定的)。

于 2014-08-18T15:18:43.893 回答
1

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

...but if I understand you, it is my only real option.

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

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

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

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

样式表

<?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". 任何以 为前缀的内容xsl:都被识别为要执行的指令,默认情况下,命名空间会从生成的 XML 中排除。

XML 输出

<document>
   <title>"One Title"</title>
   <title>
      <italics>Another Title</italics>
   </title>
</document>
于 2014-08-18T22:22:22.127 回答