【问题标题】:XSLT help - XSL for various similar XML, Template usageXSLT 帮助 - 各种类似 XML、模板用法的 XSL
【发布时间】:2011-11-11 08:32:01
【问题描述】:

我对 XSL 和 XPath 非常陌生。如果这个问题显得有些愚蠢,我们深表歉意。

我有一个类似于

的 XML
<root>
    <widget name="status">
    ...
        <component name="date">
            <component name="day" label="Fri"/>
            <component name="date" label="4"/>
        </component>
        <component name="time" label="11:23 AM"/>
    ....
    </widget>
    <widget name="foo">
    </widget>
</root>

我需要创建一个 DateTime 标记,它由所有三个值组成,例如 周五 4 上午 11:23

我正在为它编写一个 XSL。

<DateTime>
    <xsl:value-of select="(//widget[@name="status"]/component[@name='date'])[1]/@label"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="(//widget[@name="status"]/component[@name='date'])[2]/@label"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="//widget[@name="status"]/component[@name='time']/@label"/>
</DateTime>

问题:

  1. 我将“widget[@name="date"]”传递给每个 select 语句。有没有更好的方法来缩短 xpath。

  2. 我需要将它移动到模板中并调用该模板。我应该使用哪一个 call-template/apply-templates?

  3. 我们有一组类似的应用程序可以生成这些 XML。上面的 XML 来自 applicationA。 ApplicationB 可能会以稍微不同的方式显示细节,例如&lt;component name="datetime"&gt;Fri 4 11:23 AM&lt;/component&gt;。我们有近 3-4 个这样的应用程序,它们以不同的方式显示细节。

DateTime 只是一个示例,我还需要从这些不同的应用程序中获取一些其他细节。

我正在考虑编写一个 XSL 来处理所有应用程序。

【问题讨论】:

    标签: templates xslt xpath


    【解决方案1】:

    使用您的 XML 执行此操作的一种方法是:

    <xsl:template match="widget">
      <!-- ... -->
      <xsl:apply-templates select="." mode="create-date-time" />
      <!-- ... -->
    </xsl:template>
    
    <xsl:template match="widget" mode="create-date-time">
      <xsl:variable name="date" select="component[@name='date']" />
      <xsl:variable name="time" select="component[@name='time']" />
    
      <DateTime>
        <xsl:value-of select="normalize-space(
          concat(
            $date/component[@name='day']/@label, ' ', 
            $date/component[@name='date']/@label, ' ',
            $time/@label
          )
        )" />
      </DateTime>
    </xsl:template>
    
    • 我将widget[@name="date"] 传递给每个选择语句。有没有更好的方法来缩短 xpath。

    使用&lt;xsl:template&gt;/&lt;xsl:apply-templates&gt; 和相对路径。在&lt;xsl:variable&gt; 中多次存储您需要的东西。见上文。

    • 我需要将它移动到一个模板中并调用该模板。我应该使用哪一个 call-template/apply-templates?

    后者。除非有充分的理由不这样做,否则总是选择&lt;xsl:apply-templates&gt;。根据经验:如果您不确定,那就没有充分的理由。

    • 我们有一组类似的应用程序可以生成这些 XML。上面的 XML 来自 applicationA。 ApplicationB 可能会以稍微不同的方式显示细节,例如 &lt;component name="datetime"&gt;Fri 4 11:23 AM&lt;/component&gt; 我们有近 3-4 个这样的应用程序,它们以不同的方式显示细节。

    您可以扩展 create-date-time 模板以适应这种情况:

    <xsl:template match="widget" mode="create-date-time">
      <xsl:variable name="date" select="component[@name='date']" />
      <xsl:variable name="time" select="component[@name='time']" />
      <xsl:variable name="dt"   select="component[@name='datetime']" />
    
      <DateTime>
        <xsl:value-of select="normalize-space(
          concat(
            $dt/label, ' ',
            $date/component[@name='day']/@label, ' ', 
            $date/component[@name='date']/@label, ' ',
            $time/@label
          )
        )" />
      </DateTime>
    </xsl:template>
    

    如果缺少某些组件,则不会出现错误。 normalize-space() 确保任何组件组合都没有多余的空间。

    如果date+timedatetime 组件不是互斥的(我假设它们是互斥的),上述操作可能会失败。如果不是,或者出现更复杂的情况,请创建额外的特定模板,例如:

    <xsl:template match="widget[component[name='datetime']]" mode="create-date-time">
      <xsl:variable name="dt"   select="component[@name='datetime']" />
    
      <DateTime>
        <xsl:value-of select="component[@name='datetime']/@label" />
      </DateTime>
    </xsl:template>
    

    &lt;xsl:apply-templates&gt; 将确保调用正确的。只需为可能发生的每种情况创建特定的match= 表达式即可。

    【讨论】:

    • 非常感谢您的及时答复。
    • 当我在有问题的 XML 上运行第一个模板时,它会处理两次,这是错误的。我得到了两个我不想要的 标签。
    • @rpg:您可以修改create-date-time 模板,以便在小部件中没有日期/时间信息时它不会返回任何内容。这并不难。 (请注意,我已经在我的解决方案中添加了 &lt;DateTime&gt; 元素,我之前错过了它们。)
    【解决方案2】:

    这个简单的转换(16行,单个模板,完全“推”式,无变量,无模式):

    <xsl:stylesheet version="1.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=
     "component
        [contains('|day|date|time|',
                  concat('|', @name, '|'))
        ]
     ">
         <xsl:value-of select="concat(@label, ' ')"/>
         <xsl:apply-templates/>
     </xsl:template>
     <xsl:template match="text()"/>
    </xsl:stylesheet>
    

    应用于提供的 XML 文档时

    <root>
        <widget name="status">
        ...
            <component name="date">
                <component name="day" label="Fri"/>
                <component name="date" label="4"/>
            </component>
            <component name="time" label="11:23 AM"/>
        ....
        </widget>
        <widget name="foo">
        </widget>
    </root>
    

    准确地产生想要的结果

     Fri 4 11:23 AM 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-20
      • 1970-01-01
      • 2017-11-06
      • 2011-02-12
      • 2011-01-20
      • 2020-10-27
      • 2015-03-11
      • 1970-01-01
      相关资源
      最近更新 更多