基于共同属性对不同元素进行 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>
我得到 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>