【问题标题】:grouping based on the new value基于新值分组
【发布时间】:2020-11-10 05:01:21
【问题描述】:

我正在进行转换,我想通过新站点值添加值。仓库 150 和仓库 120 变成美国站点,而欧盟站点只有仓库 405。我该怎么做?

输入:

<Recordname>
    <fld1>product1</fld1>
    <fld2>warehouse150</fld2>
    <fld3>13</fld3>
</Recordname>
<Recordname>
    <fld1>product1</fld1>
    <fld2>warehouse120</fld2>
    <fld3>12</fld3>
</Recordname>
<Recordname>
    <fld1>product1</fld1>
    <fld2>warehouse405</fld2>
    <fld3>22</fld3>
</Recordname>
<Recordname>
    <fld1>product1</fld1>
    <fld2>warehouse405</fld2>
    <fld3>2</fld3>
</Recordname>
<Recordname>
    <fld1>product2</fld1>
    <fld2>warehouse150</fld2>
    <fld3>7</fld3>
</Recordname>
<Recordname>
    <fld1>product2</fld1>
    <fld2>warehouse405</fld2>
    <fld3>6</fld3>
</Recordname>

输出:

<Recordname>
    <fld1>product1</fld1>
    <site>US</site>
    <fld3>25</fld3>
</Recordname>
<Recordname>
    <fld1>product1</fld1>
    <site>EU</site>
    <fld3>24</fld3>
</Recordname>
<Recordname>
    <fld1>product2</fld1>
    <site>US</site>
    <fld3>7</fld3>
</Recordname>
<Recordname>
    <fld1>product2</fld1>
    <site>EU</site>
    <fld3>6</fld3>
</Recordname>

期待您的回答,因为我是 stylesheet 的新手。

【问题讨论】:

  • 到目前为止你尝试了什么?
  • 我确实尝试了两步 xslt,因为我不知道如何一步完成。第一个 xslt 将仅将 fld2 替换为 site warehouse150 或仓库 120 变为 US,而 warehouse405 变为 EU,然后第二个 xslt 将仅按站点和产品求和。我希望将它放在一个 xslt 中。
  • 这主要是一个分组问题(或者至少看起来是这样)。 XSLT 1.0 或 2.0 的分组方法不同。您将使用哪种处理器?
  • 对。我将使用 XSLT 2.0,但我的中间件也可以处理 1.0。如果在 2.0 中更容易,那么我宁愿这样做
  • 这里有一个 group 和 sum 的例子:stackoverflow.com/questions/2768712/…。如果示例不清楚,您可以编辑您的问题吗?它应该转化为您的输入

标签: xslt


【解决方案1】:

虽然可以一次性完成,但更容易 - 并且更易读 - 分两次完成:首先重命名 fld2 元素并使用您的站点名称填充它,然后按产品和站点对结果进行分组:

XSLT 2.0

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

<!-- identity transform (for all modes) -->
<xsl:template match="@*|node()" mode="#all">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()" mode="#current"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="/root">
     <!-- first pass -->
    <xsl:variable name="first-pass">
        <xsl:apply-templates mode="first-pass"/>
    </xsl:variable>
    <!-- output -->
    <xsl:copy>
        <!-- group by product and site -->
        <xsl:for-each-group select="$first-pass/Recordname" group-by="concat(fld1, '|', site)">
            <xsl:copy>
                <xsl:apply-templates mode="output"/>
            </xsl:copy>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<!-- rename and repopulate fld2 -->
<xsl:template match="fld2" mode="first-pass">
    <site>
        <xsl:value-of select="if(.='warehouse405') then 'EU' else 'US'"/>
    </site>
</xsl:template>

<!-- sum current group -->
<xsl:template match="fld3" mode="output">
    <xsl:copy>
        <xsl:value-of select="sum(current-group()/fld3)"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

演示https://xsltfiddle.liberty-development.net/6pS2B6F/1

【讨论】:

    【解决方案2】:

    你需要在你的 XML 中添加一个根元素,像这样

    <records>
    <Recordname>
        <fld1>product1</fld1>
        <fld2>warehouse150</fld2>
        <fld3>13</fld3>
    </Recordname>
    <Recordname>
        <fld1>product1</fld1>
        <fld2>warehouse120</fld2>
        <fld3>12</fld3>
    </Recordname>
    <Recordname>
        <fld1>product1</fld1>
        <fld2>warehouse405</fld2>
        <fld3>22</fld3>
    </Recordname>
    <Recordname>
        <fld1>product1</fld1>
        <fld2>warehouse405</fld2>
        <fld3>2</fld3>
    </Recordname>
    <Recordname>
        <fld1>product2</fld1>
        <fld2>warehouse150</fld2>
        <fld3>7</fld3>
    </Recordname>
    <Recordname>
        <fld1>product2</fld1>
        <fld2>warehouse405</fld2>
        <fld3>6</fld3>
    </Recordname>
    </records>
    

    针对您的案例使用 XSLT 的一种简单方法是:

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
        <xsl:template match="/">
            <records>
                <xsl:for-each select="records/Recordname">
                <!-- sort the nodes-->
                    <xsl:sort select="fld1" />
                    <xsl:variable name="ffld1"><xsl:value-of select="fld1" /></xsl:variable>
                    
                    <!-- for each each product -->
                    <xsl:if test="count(following-sibling::*[fld1=$ffld1])=0">
                        <!-- output US record -->
                        <Recordname>
                            <fld1><xsl:value-of select="$ffld1" /></fld1>
                            <fld2>US</fld2>
                            <fld3><xsl:value-of select="sum(//Recordname[fld1=$ffld1 and (fld2='warehouse150' or fld2='warehouse120')]/fld3)" /></fld3>
                        </Recordname>
                        <Recordname>
                        <!-- output EU record -->
                            <fld1><xsl:value-of select="$ffld1" /></fld1>
                            <fld2>EU</fld2>
                            <fld3><xsl:value-of select="sum(//Recordname[fld1=$ffld1 and (fld2='warehouse405')]/fld3)" /></fld3>
                        </Recordname>
                    </xsl:if>
                </xsl:for-each>
            </records>
        </xsl:template>
    
    </xsl:stylesheet>
    

    上面得到了你想要的输出,虽然它不如 Muenchian 方法优雅(从来没有)。

    How to group and sum values in XSLT

    【讨论】:

    • 如果 Muenchian 分组是最好的,为什么要发布一个劣质的解决方案?当 OP 可以使用 XSLT 2.0 时,为什么还要使用 XSLT 1.0 方法?您还假设每个产品都存在于两个站点中。
    • XSLT1 始终可用,为什么不呢? Muenchian 更优雅(主观),但可能不那么容易理解(也是主观的)。发布替代方案可能具有指导意义。
    • Muenchian 分组更有效 - 客观。你不知道 OP 有多少记录。当然 XSLT 2.0 分组方法更可取 - 否则他们为什么要添加它?
    • 谢谢你,我让它工作了,但我的命名空间有问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 2020-03-03
    • 2012-11-30
    • 2015-05-08
    相关资源
    最近更新 更多