【问题标题】:Convert xml using xslt使用 xslt 转换 xml
【发布时间】:2011-08-29 15:42:01
【问题描述】:

我有以下 xml:

<fo:block font-weight="bold" font-style="italic">Some Text Here</fo:block>

我需要使用 xsl 将其转换为以下内容:

{\b\iSome Text Here\i0\b0\par}

到目前为止,我设法使用以下方法选择块元素:

<xsl:template match="fo:block">
<xsl:text>{</xsl:text>
    <xsl:apply-templates />
<xsl:text>\par}</xsl:text></xsl:template>

我正在输出:{Some Text Here\par}

我正在努力处理属性并使用 xsl 插入它,谁能给我一个选择这些属性并获得所需结果的示例?

【问题讨论】:

  • 好问题,+1。请参阅我的答案以获得简单通用的完整解决方案,以便可以轻松添加新属性的处理。
  • 或者,看看我的更短的,根本没有条件指令。
  • @Dusty Roberts:我添加了我的解决方案的另一个变体,它产生的结果保留了属性的词汇顺序。 @Flynn 的解决方案真的很优雅,但是破坏了这个顺序。
  • 鉴于在这种情况下没有必要保留它,这种批评是没有根据的,特别是因为我的解决方案很容易扩展以在需要时维护该顺序。
  • @Dusty Roberts:我已经修改了我的第二个解决方案,它保持了结果的词汇顺序——它借鉴了 Flynn1179 解决方案的想法,但对其进行了改进并显着简化了它。现在我们只有 3 个模板,而不是 5 个,而且根本没有模式。最重要的是,转换会根据属性的词法顺序创建结果。

标签: xml xslt xsl-fo


【解决方案1】:

这种转换更加通用和可扩展

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

 <xsl:template match="/*">
  <xsl:variable name="vAttributesResult">
   <xsl:call-template name="processAttributes"/>
  </xsl:variable>

  <xsl:value-of select=
   "concat(substring-before($vAttributesResult, ' '),
           .,
           substring-after($vAttributesResult, ' ')
           )
   "/>
 </xsl:template>

 <xsl:template name="processAttributes">
  <xsl:param name="pattrList" select="@*"/>
  <xsl:param name="pResult" select="' '"/>

  <xsl:choose>
      <xsl:when test="not($pattrList)">
       <xsl:value-of select="$pResult"/>
      </xsl:when>
      <xsl:otherwise>
       <xsl:variable name="vthisResult">
        <xsl:apply-templates select="$pattrList[1]">
         <xsl:with-param name="pResult" select="$pResult"/>
        </xsl:apply-templates>
       </xsl:variable>

       <xsl:call-template name="processAttributes">
        <xsl:with-param name="pattrList" select="$pattrList[position()>1]"/>
        <xsl:with-param name="pResult" select="$vthisResult"/>
       </xsl:call-template>
      </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="@font-weight[.='bold']">
  <xsl:param name="pResult"/>
  <xsl:value-of select="concat('\b', $pResult, '\b0')"/>
 </xsl:template>

 <xsl:template match="@font-style[.='italic']">
  <xsl:param name="pResult"/>
  <xsl:value-of select="concat('\i', $pResult, '\i0')"/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时(更正为格式正确):

<fo:block font-weight="bold" font-style="italic"
xmlns:fo="some:fo">Some Text Here</fo:block>

产生想要的结果

\i\bSome Text Here\b0\i0

请注意

  1. 您可以根据需要轻松地为任意多的新属性/值组合添加处理 - 只需为新属性添加新模板。

  2. 如果顺序很重要,请使用这些模板来处理属性:

--

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

    <xsl:template match="/*">
        <xsl:text>{</xsl:text>
        <xsl:apply-templates select="@*">
         <xsl:with-param name="pSuff" select="''"/>
         <xsl:sort select="position()"/>
        </xsl:apply-templates>

        <xsl:apply-templates select="node()" />

        <xsl:apply-templates select="@*">
         <xsl:with-param name="pSuff" select="'0'"/>
         <xsl:sort select="position()" order="descending"/>
        </xsl:apply-templates>

        <xsl:text>\par}</xsl:text>
    </xsl:template>

    <xsl:template match="@font-weight['bold']">
      <xsl:param name="pSuff"/>
      <xsl:value-of select="concat('\b',$pSuff)"/>
    </xsl:template>

    <xsl:template match="@font-style['italic']">
      <xsl:param name="pSuff"/>
      <xsl:value-of select="concat('\i',$pSuff)"/>
    </xsl:template>
</xsl:stylesheet>

这种转换借鉴了@Flynn1169 的答案并显着简化了它(只有 3 个模板而不是 t,并且没有模式),最重要的是,根据属性的词法顺序呈现结果。

在这种情况下,结果匹配属性的词法顺序,而不是它们的排序名称! :

如果我们有这个 XML 文档

    <fo:block font-style="italic" font-weight="bold" 
xmlns:fo="some:fo">Some Text Here</fo:block>

现在的结果是:

{\i\bSome Text Here\b0\i0\par}

备注:虽然在 XPath 数据模型中没有任何“属性顺序”,但我在推式模式下使用的所有 XSLT 处理器(超过 9 个)都会产生处理结果属性根据它们的词法顺序。

【讨论】:

    【解决方案2】:

    一般来说有一种相当简单的方法,使用模板模式和&lt;xsl:sort&gt; 指令。

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template match="fo:block">
      <xsl:text>{</xsl:text>
      <xsl:apply-templates select="@*" mode="prefix" />
      <xsl:apply-templates select="node()" />
      <xsl:apply-templates select="@*" mode="suffix">
        <xsl:sort order="descending"/>
      </xsl:apply-templates>
      <xsl:text>\par}</xsl:text>
    </xsl:template>
    
    <xsl:template match="@font-weight['bold']" mode="prefix">\b</xsl:template>
    <xsl:template match="@font-style['italic']" mode="prefix">\i</xsl:template>
    
    <xsl:template match="@font-weight['bold']" mode="suffix">\b0</xsl:template>
    <xsl:template match="@font-style['italic']" mode="suffix">\i0</xsl:template>
    
    </xsl:stylesheet>
    

    &lt;xsl:sort order="descending" /&gt; 第二次以相反的顺序处理属性,当使用 'suffix' 模式时。

    严格来说,主模板中间的select="node()"是多余的,但读起来更清楚,只处理节点,不处理属性。

    您可以通过将现有的suffix 模式模板替换为以下内容来更轻松地添加新属性:

    <xsl:template match="@*" mode="suffix">
      <xsl:apply-templates select="." mode="prefix" />
      <xsl:text>0</xsl:text>
    </xsl:template>
    

    这只是使用前缀的模板,并在末尾添加额外的0。如果某些属性不能像这样通用处理,您可以随时覆盖它。

    【讨论】:

    • 每个属性都要生成两次输出,这样看来不无道理。
    • @flynn1179:另一个问题(对于这个特定问题可能并不重要)是输出顺序与属性的词汇顺序无关。是的,我们都知道属性顺序只是一个词法属性,但是我使用的所有 9 个 XSLT 处理器都会根据它们的词法顺序生成属性的推送式处理结果。在某些情况下,保持这种顺序对人类读者很重要。
    • @Dimitre,很明显在这种情况下这无关紧要。如果词法顺序相关,那么将&lt;xsl:sort select="name()" /&gt; 和相同的选择属性添加到降序排序显然是微不足道的。通常,如果顺序相关,则建议明确编码,以防止跨平台不一致。
    • @Flynn1179:我实际上制作了一个新的订单敏感解决方案,借鉴了您的解决方案的想法,但大大简化了它 - 更少的模板(只有 3 对 5)并且根本没有模式。并且没有明确的&lt;xsl:apply-templates&gt; 序列来保持顺序,因此您的建议实际上没有必要。
    【解决方案3】:
    <xsl:template match="fo:block">
        <xsl:text>{</xsl:text>
            <xsl:if test="@font-weight = 'bold'">\b</xsl:if>
            <xsl:if test="@font-style = 'italic'">\i</xsl:if>
    
            <xsl:apply-templates />
    
            <xsl:if test="@font-style = 'italic'">\i0</xsl:if>
            <xsl:if test="@font-weight = 'bold'">\b0</xsl:if>
        <xsl:text>\par}</xsl:text>
    </xsl:template/>
    

    还可以查看w3schools.com 以获取有关 XSLT 的更多信息。

    【讨论】:

    • 这很好,谢谢,但也许没有办法让 if 语句更通用一点,即:也许有办法循环遍历属性?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多