基于共同属性对不同元素进行 XSLT Muenchian 分组

XSLT Muenchian Grouping on different elements based on a common attribute

我得到 XML 类似于我需要处理的以下内容。

<root>
    <Header/>
    <Customer id="1" date="13/04/2014"/>
    <Account id="1" date="14/04/2014"/>
    <Account id="1" date="01/06/2015"/>
    <Address id="1" date="14/04/2014"/>
    <Customer id="2" date="12/08/2015"/>
    <Account id="2" date="13/08/2015"/>
    <Address id="2" date="13/08/2015"/>
    <Address id="2" date="03/09/2015"/>
    <Address id="2" date="27/01/2017"/>
    <Customer id="3" date="04/10/2015"/>
    <Customer id="3" date="01/02/2017"/>
    <Account id="3" date="05/10/2015"/>
    <Address id="3" date="08/10/2015"/>
    <Address id="3" date="03/09/2016"/>
</root>

所有的节点都有更多的属性,但我把它们去掉了。每个元素都有一个 id 和一个 date.If 有重复的元素具有相同的 id 那么具有最近日期的元素被认为是有效的并且应该忽略较旧的元素。

如果旧的可以同时剥离出来我想输出成这样

<Customers>
    <Customer id="1">
        <Account/>
        <Address/>
    </Customer>
    <Customer id="2">
        <Account/>
        <Address/>
    </Customer>
    <Customer id="3">
        <Account/>
        <Address/>
    </Customer>
</Customers>

如果不是,则可以在两个转换中处理文件(一个按客户 ID 对它们进行分组,每个客户有多个 Account/Address 字段,然后在另一个转换中删除较旧的条目)

来源 XML 有将近一百万个条目,因此性能是一个问题。转换几分钟没问题,但超过 15 分钟就不行了。

我目前有以下 XSLT

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

    <xsl:key name="nodes-by-id" match="//root/*" use="@id"/>

    <xsl:template match="root">
        <Customers>
            <xsl:for-each select="*[count(. | key('nodes-by-id', @id)[1]) = 1]">
                <xsl:variable name="current-grouping-key" select="@id"/>
                <xsl:variable name="current-group" select="key('nodes-by-id', $current-grouping-key)"/>
                <Customer>
                    <xsl:attribute name="id">
                        <xsl:value-of select="$current-grouping-key"/>
                    </xsl:attribute>
                    <CustomerElements>
                        <xsl:for-each select="$current-group/Customer">
                            <CustomerElement>
                                <xsl:attribute name="date">
                                    <xsl:value-of select="@date"/>
                                </xsl:attribute>
                            </CustomerElement>
                        </xsl:for-each>
                    </CustomerElements>
                    <xsl:apply-templates select="$current-group"/>
                </Customer>
            </xsl:for-each>
        </Customers>
    </xsl:template>
</xsl:stylesheet>

目前这只是尝试按 id 对所有元素进行分组,然后输出所有 Customer 元素。我得到以下信息:

<Customers>
    <Customer id="">
        <CustomerElements/>
    </Customer>
    <Customer id="1">
        <CustomerElements/>
    </Customer>
    <Customer id="2">
        <CustomerElements/>
    </Customer>
    <Customer id="3">
        <CustomerElements/>
    </Customer>
</Customers>

我得到了 ID 为空的客户,因为我没有忽略 header 行。我真正的问题是为什么 $current-group 变量不包含任何元素?

还有关于如何忽略 header 行以及过滤掉日期较早的条目的任何提示。

我把所有东西都整理好了。这是我使用的 XSLT 的一部分。 XML 评论中有更多信息。

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

    <xsl:key name="nodes-by-id" match="//root/*" use="@id"/>

    <xsl:template match="PR-030">
        <CustomerMeters>
        <!-- Using select="Customer[cou.... instead of select="*[cou... will couse it to ignore the header. However it requres
            the Customer element to be the first element for the icp in the xml. -->
            <xsl:for-each select="Customer[count(. | key('nodes-by-id', @id)[1]) = 1]">
                <xsl:variable name="current-grouping-key" select="@id"/>
                <xsl:variable name="current-group" select="key('nodes-by-id', $current-grouping-key)"/>

                <xsl:variable name="current-group-sorted">
                    <!-- If we sort all nodes by date order, then we can fetch the first Address/Customer/etc... from this group and we will have the latest-->
                    <xsl:for-each select="$current-group">
                        <!-- year -->
                        <xsl:sort select="substring(@date, 7, 4)" order="descending" data-type="number"/>
                        <!-- month -->
                        <xsl:sort select="substring(@date, 4, 2)" order="descending" data-type="number"/>
                        <!-- day -->
                        <xsl:sort select="substring(@date, 1, 2)" order="descending" data-type="number"/>
                        <xsl:copy-of select="current()" />
                    </xsl:for-each>
                </xsl:variable>
                <Customer>
                    <!-- In here I can get what I want from the current-group-sorted varaible-->
                    <!-- Because they are in date order I can just get the first occurance and it will be the most recent-->
                    <someField>
                        <xsl:value-of select="$current-group-sorted/*[self::Account][1]/@someAttribute"/>
                    </someField>
                </Customer>
            </xsl:for-each>
        </CustomerMeters>
    </xsl:template>
</xsl:stylesheet>