XSLT:for-each-group 将一组 xml 个节点转换为并发变量
XSLT : for-each-group to turn a set of xml nodes into concurrent variables
我正在努力进行以下转换。
我的来源是一个 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 根本不会对任何内容进行分组。
因此,我无法同时填充 year/index 组合的所有月份。
如有任何帮助,我们将不胜感激,请随时寻求进一步的解释/上下文/输入/示例。
此致,
我发现您的 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>
我正在努力进行以下转换。
我的来源是一个 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 根本不会对任何内容进行分组。 因此,我无法同时填充 year/index 组合的所有月份。
如有任何帮助,我们将不胜感激,请随时寻求进一步的解释/上下文/输入/示例。
此致,
我发现您的 XSLT 存在一些问题...
- 您在 for-each-group 中选择
/IndexGroup
,这意味着 XML 的根元素必须是IndexGroup
。 - 您尝试将
IndexDate
转换为xs:date
,但格式DD/MM/YYYY
不是有效的 xs:date。 - 您正在为
IndexNumbers
的子级创建 - 由于您按
IndexRate
分组,然后按IndexRate
+IndexYear
,我认为您需要为每个组做两个单独的分组。
xsl:variable
而不是文字结果元素
这是我会做的...
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>