【问题标题】:XSLT : for-each-group to turn a set of xml nodes into concurrent variablesXSLT : for-each-group 将一组 xml 节点转换为并发变量
【发布时间】:2018-05-27 13:01:54
【问题描述】:

我正在努力进行以下转换。

我的源是一个我不知道根节点的 XML(它是由 ETL 动态创建的,我无法输出 XML。因此是 presumed_root)。

XML 源

<presumed_root>
    <IndexGroup>
        <IndexDate>01/01/2017</IndexDate>
        <IndexRate>US_CPI</IndexRate>
        <IndexValue>190</IndexValue>
    </IndexGroup>
    <IndexGroup>
        <IndexDate>01/04/2017</IndexDate>
        <IndexRate>US_CPI</IndexRate>
        <IndexValue>195</IndexValue>
    </IndexGroup>
    <IndexGroup>
        <IndexDate>01/07/2017</IndexDate>
        <IndexRate>US_CPI</IndexRate>
        <IndexValue>193</IndexValue>
    </IndexGroup>
    [...]
</presumed_root>

所需的 XML 输出

<root>
<IndexGroup>
    <IndexRate>US_CPI</IndexRate>
    <IndexNumbers>
        <IndexCode>US_CPI@2017</IndexCode>
        <IndexYear>2017</IndexYear>
        <Month01>190</Month01>
        <Month02/>
        <Month03/>
        <Month04>195</Month04>
        <Month05/>
        <Month06/>
        <Month07>193</Month07>
        [...]
    </IndexNumbers>
</IndexGroup>
</root>

所以,我尝试按 IndexRate 和 IndexYear 对值进行分组,并将每个月放在一个不同的节点中。

XSL 失败

 <xsl:template match="/">

<xsl:for-each-group select="/IndexGroup" group-by="concat(translate(normalize-space(IndexRate),' ','_'),'@',xs:string(year-from-date(xs:date(IndexDate))))"> 
<IndexGroup>

    <xsl:variable name="IndexCode" select="current-group()/translate(normalize-space(IndexRate),' ','_')"/>
    <IndexRate><xsl:value-of select="$IndexCode"/></IndexRate>
    <IndexNumbers>
            <xsl:variable name="IndexCode"><xsl:value-of select="current-grouping-key()"/></xsl:variable><!-- Code = Index @ Year -->
            <xsl:variable name="Month01"><xsl:value-of select="translate(replace(current-group()[month-from-date(xs:date(current-group()/IndexDate))=1]/IndexValue, '\p{Z}+', ''),',','.')"/></xsl:variable>
            <xsl:variable name="Month02"><xsl:value-of select="translate(replace(current-group()[month-from-date(xs:date(current-group()/IndexDate))=2]/IndexValue, '\p{Z}+', ''),',','.')"/></xsl:variable>
        [...]           
    </IndexNumbers>
[...]
</IndexGroup>

使用这种 XML 结构和 XSL,for-each-group 根本不会对任何内容进行分组。 因此,我无法同时填充一年中的所有月份/索引组合。

任何帮助将不胜感激,请随时要求进一步的解释/上下文/输入/示例。

问候,

【问题讨论】:

  • 我的回答有帮助还是您还有问题?
  • 嗨丹尼尔,感谢您的解决方案。
  • 唉,我仍然遇到问题:使用相同的源和 XSL,我没有与您相同的结果。更具体地说,for-each-group 不是分组。这导致我有 2 个可能的原因: - 源 XML 没有根元素(可能,因为我不知道 IndexGroup 节点上方是什么) - 或者嵌入式 XSL 解释器中存在错误(我没有可以访问产品代码,因为我只实现了这个产品)。我将只花几个小时调查第一个案例。不然我就放弃了……
  • 这两种解释在我看来都不太可能,但我们无法诊断您的问题,除非我们确切地知道您在做什么以及它是如何失败的,并且您没有向我们提供足够的信息。由于我认为您是初学者,因此您甚至可能错误地调用了转换,因此说明您是如何运行它会有所帮助。
  • 产品开发团队已确认每个IndexGroup节点没有封闭的顶部节点。因此我的“presumed_root”确实是无效的。通过对数据源本身的智能调整,我成功地使用了您的大部分代码。您的解决方案是正确的,非常有用且聪明,因此被接受!再次感谢。

标签: xml xslt xslt-grouping


【解决方案1】:

我发现您的 XSLT 存在一些问题...

  • 您在 for-each-group 中选择 /IndexGroup,这意味着您的 XML 的根元素必须是 IndexGroup
  • 您尝试将 IndexDate 转换为 xs:date,但格式 DD/MM/YYYY 不是有效的 xs:date。
  • 您正在为IndexNumbers 的子级创建xsl:variable 而不是文字结果元素
  • 由于您按IndexRate 分组,然后按IndexRate + IndexYear 分组,我认为您需要为每个组分别做两个。

这就是我要做的...

XML 输入

<presumed_root>
    <IndexGroup>
        <IndexDate>01/01/2017</IndexDate>
        <IndexRate>US_CPI</IndexRate>
        <IndexValue>190</IndexValue>
    </IndexGroup>
    <IndexGroup>
        <IndexDate>01/04/2017</IndexDate>
        <IndexRate>US_CPI</IndexRate>
        <IndexValue>195</IndexValue>
    </IndexGroup>
    <IndexGroup>
        <IndexDate>01/07/2017</IndexDate>
        <IndexRate>US_CPI</IndexRate>
        <IndexValue>193</IndexValue>
    </IndexGroup>
    [...]
</presumed_root>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/*">
    <root>
      <xsl:for-each-group select="IndexGroup" group-by="IndexRate">
        <xsl:copy>
          <xsl:copy-of select="IndexRate"/>
          <xsl:for-each-group select="current-group()" 
            group-by="concat(IndexRate,'@',tokenize(IndexDate,'/')[last()])">
            <IndexNumbers>
              <xsl:apply-templates select="@*"/>
              <IndexCode>
                <xsl:value-of select="current-grouping-key()"/>
              </IndexCode>
              <IndexYear>
                <xsl:value-of select="tokenize(IndexDate,'/')[last()]"/>
              </IndexYear>
              <xsl:for-each select="1 to 12">
                <xsl:variable name="month" select="format-number(.,'00')"/>
                <xsl:variable name="pattern" select="concat('\d{2}/',$month,'/\d{2}')"/>
                <xsl:element name="month{$month}">
                  <xsl:value-of 
                    select="current-group()[matches(IndexDate,$pattern)]/IndexValue"/>
                </xsl:element>
              </xsl:for-each>
            </IndexNumbers>
          </xsl:for-each-group>          
        </xsl:copy>
      </xsl:for-each-group>
    </root>
  </xsl:template>

</xsl:stylesheet>

输出

<root>
   <IndexGroup>
      <IndexRate>US_CPI</IndexRate>
      <IndexNumbers>
         <IndexCode>US_CPI@2017</IndexCode>
         <IndexYear>2017</IndexYear>
         <month01>190</month01>
         <month02/>
         <month03/>
         <month04>195</month04>
         <month05/>
         <month06/>
         <month07>193</month07>
         <month08/>
         <month09/>
         <month10/>
         <month11/>
         <month12/>
      </IndexNumbers>
   </IndexGroup>
</root>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-26
    • 1970-01-01
    • 2019-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多