XSL转型麻烦

时间:2013-12-27 11:33:18

标签: xml xslt

我在使用XSL样式表转换XML时遇到一些麻烦,其中输出需要的结构不符合XML文件中的结构。

简单示例:

输入:

<p>This is <b>bold text</b>, <i>italic text</i> and normal text</p>

我的问题是输入中的字符格式是嵌套的,并且它们不能嵌套在输出中(没有任何明显格式化的文本被认为具有“普通”字符样式,并且必须在输出中定义。

有效输出:

<p>
   <c style="normal">This is </c>
   <c style="bold">bold text</c>
   <c style="normal">, </c>
   <c style="italic">italic text</c>
   <c style="normal"> and normal text</c>
</p>

如何做到这一点?

我可以弄清楚如何获得粗体和斜体格式,但不知道如何在从其他人返回之后开始新的“普通”格式,因为我无法保存当前状态。请注意,必须遵循“粗体”或“斜体”不是必需的“正常”,它是粗体或斜体之前的活动格式,所以我真正想要的是记住我当前使用的格式的一些方法,这样我可以在使用其他字符格式后再次定义它。

请注意,以下(明显)样式无效,因为它包含嵌套格式。我没有创建它的问题:

<p>
   <c style="normal">This is
      <c style="bold">bold text</c>
      , 
      <c style="italic">italic text</c>
      and normal text
   </c>
</p>

2 个答案:

答案 0 :(得分:2)

我会通过定义不是元素的模板而是为它们内部的文本节点定义模板,使用每个文本节点的祖先来计算它应该是style的内容:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="yes" />

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

  <!-- direct text nodes under a p become "normal" -->
  <xsl:template match="p/text()">
    <c style="normal"><xsl:value-of select="." /></c>
  </xsl:template>

  <!-- text nodes under both an i and a b become bold-italic -->
  <xsl:template match="b//i/text() | i//b/text()" priority="2">
    <c style="bold-italic"><xsl:value-of select="." /></c>
  </xsl:template>

  <!-- text nodes under a b but not an i become bold -->
  <xsl:template match="b/text()">
    <c style="bold"><xsl:value-of select="." /></c>
  </xsl:template>

  <!-- text nodes under an i but not a b become italic -->
  <xsl:template match="i/text()">
    <c style="italic"><xsl:value-of select="." /></c>
  </xsl:template>

</xsl:stylesheet>

bi元素本身将使用默认的内置模板规则,该规则只执行<xsl:apply-templates/>(即,不会专门为此节点输出,而是继续处理子节点)。

或者,您可以为元素定义模板,并使用参数将“当前状态”从一个模板传递到下一个模板,当您沿树工作时。如果您有XSLT 2.0,那么这对"tunnel" parameters很有用:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                exclude-result-prefixes="xs">
  <xsl:output indent="yes" />

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

  <!-- output text nodes as <c style="current-style">.  The $style parameter
       is a sequence of strings giving the current nested styles, in order.
       Therefore <b><i>some text</i></b> would be bold-italic whereas
       <i><b>some text</b></i> would be italic-bold. -->
  <xsl:template match="text()">
    <xsl:param tunnel="yes" name="style" as="xs:string*" />
    <!-- use "normal" if no current style -->
    <c style="{if (count($style)) then string-join($style, '-') else 'normal'}">
      <xsl:value-of select="."/>
    </c>
  </xsl:template>

  <xsl:template match="b">
    <xsl:param tunnel="yes" name="style" as="xs:string*" />
    <xsl:apply-templates>
      <xsl:with-param tunnel="yes" name="style" select="($style, 'bold')" />
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="i">
    <xsl:param tunnel="yes" name="style" as="xs:string*" />
    <xsl:apply-templates>
      <xsl:with-param tunnel="yes" name="style" select="($style, 'italic')" />
    </xsl:apply-templates>
  </xsl:template>

</xsl:stylesheet>

当您$style时,决定将apply-templates参数作为param参数传递的逻辑可能会非常复杂。

如果您仅限于XSLT 1.0,那么您没有隧道参数,因此您必须在所有级别显式with-param和{{1}}元素,但这应该会给您一些想法如何进行。

答案 1 :(得分:0)

这可以解决您的问题:

'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
  <!-- identity-copy template (see http://en.wikipedia.org/wiki/Identity_transform#Using_XSLT ) -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <!-- transformation b - bold -->
  <xsl:template match="b">
    <c style="bold">
      <xsl:apply-templates/>
    </c>
  </xsl:template>
  <!-- transformation i - italic -->
  <xsl:template match="i">
    <c style="italic">
      <xsl:apply-templates/>
    </c>
  </xsl:template>
  <!-- if text does not have b or i as an ancestor, it is normal -->
  <xsl:template match="text()[not(ancestor::b) and not(ancestor::i)]">
    <c style="normal">
      <xsl:copy/>
    </c>
  </xsl:template>
</xsl:stylesheet>

这是一个groovy脚本,显示它是如何工作的:https://gist.github.com/akhikhl/8146185