【问题标题】:Transforming a sequence of tags using XSL使用 XSL 转换标签序列
【发布时间】:2011-01-07 06:11:11
【问题描述】:

我对使用XSL 有点陌生。我正在尝试使用 XSL 将 XML 文件转换为具有不同结构的另一个 XML 文件。 XML 的 input 部分如下所示:

<tag>
    <Keyword>Event: Some Text</Keyword>
    <Keyword>Group: Some Text</Keyword>
    <Keyword>Other: Some Text</Keyword>
</tag>

我希望所需的输出是:

<tag>
    <event> Some Text </event>
    <group> Some Text </group>
    <other> Some Text </other>
</tag>

我的当前 XSL 文件:

<xsl:for-each select="tag">
    <xsl:if test="starts-with(Keyword, 'Event: ')">
        <event>
          <xsl:value-of select="substring-after(Keyword, 'Event: ')"/>
        </event>
    </xsl:if>
    <xsl:if test="starts-with(Keyword, 'Group: ')">
        <group>
          <xsl:value-of select="substring-after(Keyword, 'Group: ')"/>
        </group>
    </xsl:if>
    <xsl:if test="starts-with(Keyword, 'Other: ')">
        <other>
          <xsl:value-of select="substring-after(Keyword, 'Other: ')"/>
        </other>
    </xsl:if>
</xsl:for-each>

当前输出只显示事件节点,不显示剩余节点:

<tag>
    <event> Some Text </event>
</tag>

我尝试在 XSL 中将“组”部分与“事件”部分切换,但是可能由于输入 XML 中关键字节点的顺序,所有子节点都没有显示。那么如何读取所有关键字节点并将它们转换为相应的新节点进行显示?

【问题讨论】:

    标签: xml xslt if-statement foreach


    【解决方案1】:

    您只有一个 tag 元素。你想遍历它的所有 children

    <xsl:for-each select="tag/*">
    
        <xsl:if test="starts-with(., 'Event: ')">
            <event>
              <xsl:value-of select="substring-after(., 'Event: ')"/>
            </event>
        </xsl:if>
        <xsl:if test="starts-with(., 'Group: ')">
            <group>
              <xsl:value-of select="substring-after(., 'Group: ')"/>
            </group>
        </xsl:if>
        <xsl:if test="starts-with(., 'Other: ')">
            <other>
              <xsl:value-of select="substring-after(., 'Other: ')"/>
            </other>
        </xsl:if>
    </xsl:for-each>
    

    EDIT:添加了另一个解决方案(使用@Pierre 的原始解决方案)。
    注意 xsl:element name 属性的值包裹在{} 中,因此它被评估为Attribute Value Template,而不是文字字符串:

    <xsl:template match="tag">
    <xsl:apply-templates select="Keyword"/>
    </xsl:template>
    
    <xsl:template match="Keyword">
    <xsl:element name="{normalize-space(substring-before(.,':'))}">
     <xsl:value-of select="normalize-space(substring-after(.,':'))"/>
    </xsl:element>
    </xsl:template>
    

    【讨论】:

    • 您应该查看@Pierre 的解决方案并考虑尝试在您的XSLT 中进行比for-each 语句更多的模板匹配。这是一种不同的做事方式(声明式与程序式),起初有时很难掌握,但它会使开发和维护变得容易得多。
    【解决方案2】:

    这是我的解决方案。您不必使用 xsl:element

    创建所有这些 if 语句
    <xsl:template match="tag">
    <xsl:apply-templates select="Keyword"/>
    </xsl:template>
    
    <xsl:template match="Keyword">
     <xsl:variable name="tag" select="normalize-space(substring-before(.,':'))"/>
     <xsl:value-of select="concat('&lt;',$tag,'&gt;')" disable-output-escaping="yes"/>
     <xsl:value-of select="normalize-space(substring-after(.,':'))"/>
     <xsl:value-of select="concat('&lt;/',$tag,'&gt;')" disable-output-escaping="yes"/>
    </xsl:template>
    

    【讨论】:

    • +1 更干净。我打算在我的答案中添加类似的东西,但你打败了我!
    • 啊...你可能是对的。我用 @disable-output-escaping='yes' 将 xsl:element 更改为 xsl:value-of。
    • 为什么不使用 xsl:element 和 xsl:attribute 在运行时动态构造元素?它对于元素构造要好得多,但是,在这种情况下,简单的连接可能也可以工作
    • Alexey,这里我们需要生成元素的名称,而不是它的属性之一。
    • 我更喜欢原始代码。您需要做的就是将表达式包装在{} 中,以便将该表达式作为属性值模板进行评估。禁用输出转义以堵塞输出中的尖括号是一种不应该使用的邪恶技巧。
    【解决方案3】:

    @Pierre:我正确理解最初的问题和您的解决方案;) 恕我直言,您构建 XML 元素的方式有点 hackly。 XSLT 具有用于动态构造 xml 元素的特殊设置。所以你的 template match="Keyword" 可以是这样的

    <xsl:template match="Keyword">
      <xsl:variable name="tag" select="normalize-space(substring-before(.,':'))"/>
      <xsl:element name="{$tag}">
        <xsl:value-of select="normalize-space(substring-after(.,':'))"/>
      </xsl:element>
    </xsl:template>
    

    因此,如果需要,您可以选择轻松地将属性添加到输出 XML 元素,并且不会为禁用输出转义等而烦恼。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-24
      • 2012-08-12
      • 2013-06-14
      相关资源
      最近更新 更多