Xml XSLT在转换时查找重复值

Xml XSLT在转换时查找重复值,xml,xslt,xslt-1.0,grouping,Xml,Xslt,Xslt 1.0,Grouping,我有以下XML: <?xml version="1.0" encoding="utf-8"?> <NewDataSet> <GUID> <Active>true</Active> <ContractName>Contract Name</ContractName> <ContractNumber>Auto</ContractNumber&g

我有以下XML:

<?xml version="1.0" encoding="utf-8"?>
<NewDataSet>
    <GUID>
        <Active>true</Active>
        <ContractName>Contract Name</ContractName>
        <ContractNumber>Auto</ContractNumber>
        <DateOfBirth>16/01/1988</DateOfBirth>
        <FirstName>Fred</FirstName>
        <Notes>some notes</Notes>
        <PlaceOfResidence>United Kingdom</PlaceOfResidence>
        <RowNumber>1</RowNumber>
        <TableName>PersonDetails</TableName>
    </GUID>
    <GUID>
        <Active>true</Active>
        <ContractName>Contract Name</ContractName>
        <ContractNumber>Auto</ContractNumber>
        <DateOfBirth>01/01/1960</DateOfBirth>
        <FirstName>Harold</FirstName>
        <Notes>some notes</Notes>
        <PlaceOfResidence>United Kingdom</PlaceOfResidence>
        <RowNumber>2</RowNumber>
        <TableName>PersonDetails</TableName>
    </GUID>
    <GUID>
        <Active>true</Active>
        <ContractName>Contract Name</ContractName>
        <ContractNumber>Auto</ContractNumber>
        <DateOfBirth>05/05/1955</DateOfBirth>
        <FirstName>Mary</FirstName>
        <Notes>some notes</Notes>
        <PlaceOfResidence>United States</PlaceOfResidence>
        <RowNumber>3</RowNumber>
        <TableName>PersonDetails</TableName>
    </GUID>
    <GUID>
        <ContractName>Contract Name</ContractName>
        <ContractNumber>Auto</ContractNumber>
        <CoverType>Property</CoverType>
        <DateAdded>01/06/2017</DateAdded>
        <Notes>some notes</Notes>
        <RowNumber>1</RowNumber>
        <TableName>Covers</TableName>
    </GUID>
    <GUID>
        <ContractName>Contract Name</ContractName>
        <ContractNumber>Auto</ContractNumber>
        <CoverType>Motor</CoverType>
        <DateAdded>01/06/2017</DateAdded>
        <Notes>some notes</Notes>
        <RowNumber>2</RowNumber>
        <TableName>Covers</TableName>
    </GUID>
    <GUID>
        <ContractName>Contract Name</ContractName>
        <ContractNumber>Auto</ContractNumber>
        <CoverType>Liability</CoverType>
        <DateAdded>01/06/2017</DateAdded>
        <Notes>some notes</Notes>
        <RowNumber>3</RowNumber>
        <TableName>Covers</TableName>
    </GUID>
</NewDataSet>

真的
合同名称
自动的
16/01/1988
弗莱德
一些注释
大不列颠联合王国
1.
个人信息
真的
合同名称
自动的
01/01/1960
哈罗德
一些注释
大不列颠联合王国
2.
个人信息
真的
合同名称
自动的
05/05/1955
玛丽
一些注释
美国
3.
个人信息
合同名称
自动的
财产
01/06/2017
一些注释
1.
盖子
合同名称
自动的
发动机
01/06/2017
一些注释
2.
盖子
合同名称
自动的
责任
01/06/2017
一些注释
3.
盖子
我需要将其转换为以下内容:

<data>
    <ContractName>Contract Name</ContractName>
    <ContractNumber>Auto</ContractNumber>
    <Table>
        <TableRow RowNumber="1" TableName="PersonDetails">
            <FirstName>Fred</FirstName>
            <PlaceOfResidence>United Kingdom</PlaceOfResidence>
            <DateOfBirth>16/01/1988</DateOfBirth>
            <Active>true</Active>
        </TableRow>
        <TableRow RowNumber="2" TableName="PersonDetails">
            <FirstName>Harold</FirstName>
            <PlaceOfResidence>United Kingdom</PlaceOfResidence>
            <DateOfBirth>01/01/1960</DateOfBirth>
            <Active>true</Active>
        </TableRow>
        <TableRow RowNumber="3" TableName="PersonDetails">
            <FirstName>Mary</FirstName>
            <PlaceOfResidence>United States</PlaceOfResidence>
            <DateOfBirth>05/05/1955</DateOfBirth>
            <Active>true</Active>
        </TableRow>
    </Table>
    <Table>
        <TableRow RowNumber="1" TableName="Covers">
            <CoverType>Property</CoverType>
            <DateAdded>01/06/2017</DateAdded>
        </TableRow>
        <TableRow RowNumber="2" TableName="Covers">
            <CoverType>Motor</CoverType>
            <DateAdded>01/06/2017</DateAdded>
        </TableRow>
        <TableRow RowNumber="3" TableName="Covers">
            <CoverType>Liability</CoverType>
            <DateAdded>01/06/2017</DateAdded>
        </TableRow>
    </Table>
    <Notes>some notes</Notes>
</data>

合同名称
自动的
弗莱德
大不列颠联合王国
16/01/1988
真的
哈罗德
大不列颠联合王国
01/01/1960
真的
玛丽
美国
05/05/1955
真的
财产
01/06/2017
发动机
01/06/2017
责任
01/06/2017
一些注释
我只能使用XSLT1.0

到目前为止,我已经:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-16"/>
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*[(*)]">
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="/">
<data>
    <xsl:apply-templates select="@* | node()"/>
</data>
</xsl:template>
</xsl:stylesheet>

它将剥离
标记并替换为

但是,我不确定如何生成2个表分组,以及如何自动*标识重复值:ContractName、ContractNumber和Notes

*以后可能会出现其他重复值


任何帮助或指点都将不胜感激

这里的部分挑战是您有一个
GUID
元素的平面列表,您需要根据
TableName
值对这些元素进行分组。这似乎是一个非常经典的Muenchian分组方法,Jenny Tennison的网站上详细描述了这一技术:

下面是一些XSL代码,它们生成的输出几乎与所需的XML格式相同——唯一的区别在于
中元素的顺序。然而,这种转变有许多不明确的方面。我已经在代码注释中指出了这些问题

这个
$kids
变量是我们确定所有
GUID
结构共用的
GUID
子项的关键部分。在XSL 1.0中可能有一种更优雅、更高效的方法来实现这一点;在OxygenXML(使用Saxon HE 9.6.0.7处理器)中,对小型数据集运行此操作需要0.8秒

更新了
$nocopy
,以包含
$kids
标识的元素名称

<!-- Begin at the beginning: the root and topmost element. -->
<xsl:template match="/NewDataSet">
    <data>
        <!-- We'll put common elements at the top of the `data` structure. -->
        <xsl:copy-of select="$kids"/>

        <!-- We want to process `GUID`s after grouping by `TableName` values.
        This `for-each` is part of the Muenchian grouping technique.  See
        Jenny Tennison's page (linked above) for a detailed explanation. -->
        <xsl:for-each select="GUID[count(. | key('table', TableName)[1]) = 1]">
            <!-- If you wanted to sort alphabetically by TableName, you'd use:
            <xsl:sort select="TableName" /> -->
            <Table>
                <!-- Now, within each `table`, we want to process all those
                `GUID`s with this same corresponding `TableName`. -->
                <xsl:for-each select="key('table', TableName)">
                    <!-- We select "this", since we want to process the matching
                    `GUID`, not just it's children. -->
                    <xsl:apply-templates select="."/>
                </xsl:for-each>
            </Table>
        </xsl:for-each>
    </data>

</xsl:template>
<!-- List up the elements we don't want to copy verbatim into each table row -->
<xsl:variable name="nocopy">
    <item>RowNumber</item>
    <item>TableName</item>
    <!-- Copy in the bits from $kids -->
    <xsl:for-each select="$kids/*">
        <item><xsl:value-of select="name(.)"/></item>
    </xsl:for-each>
</xsl:variable>

<xsl:template match="GUID">
    <TableRow RowNumber="{RowNumber}" TableName="{TableName}">
        <!-- Copy over child data, but _only_ if it's not in `$nocopy` -->
        <xsl:copy-of select="*[not(name() = $nocopy/item)]"/>
    </TableRow>
</xsl:template>

行数
表名
现在,这将生成与所需输出XML功能相同的输出。唯一的区别是元素的顺序——
TableRow
子元素的顺序不同,
Notes
位于
data
的顶部,与
ContractName
ContractNumber
并列,而不是位于
data
的底部

关于输出XML数据格式的说明 将表名作为属性包含在每个表行中似乎有点奇怪。将其作为
元素本身的一个属性会更有意义

类似地,在每一行上都有
RowNumber
属性似乎是多余的。只需查看父级
表中每个
表行的
位置()
,即可收集此信息


也就是说,您知道自己的需求。那么,这只是让事情正常运转的问题。:)

这里的部分挑战是您有一个
GUID
元素的平面列表,您需要根据
TableName
值对这些元素进行分组。这似乎是一个非常经典的Muenchian分组方法,Jenny Tennison的网站上详细描述了这一技术:

下面是一些XSL代码,它们生成的输出几乎与所需的XML格式相同——唯一的区别在于
中元素的顺序。然而,这种转变有许多不明确的方面。我已经在代码注释中指出了这些问题

这个
$kids
变量是我们确定所有
GUID
结构共用的
GUID
子项的关键部分。在XSL 1.0中可能有一种更优雅、更高效的方法来实现这一点;在OxygenXML(使用Saxon HE 9.6.0.7处理器)中,对小型数据集运行此操作需要0.8秒

更新了
$nocopy
,以包含
$kids
标识的元素名称

<!-- Begin at the beginning: the root and topmost element. -->
<xsl:template match="/NewDataSet">
    <data>
        <!-- We'll put common elements at the top of the `data` structure. -->
        <xsl:copy-of select="$kids"/>

        <!-- We want to process `GUID`s after grouping by `TableName` values.
        This `for-each` is part of the Muenchian grouping technique.  See
        Jenny Tennison's page (linked above) for a detailed explanation. -->
        <xsl:for-each select="GUID[count(. | key('table', TableName)[1]) = 1]">
            <!-- If you wanted to sort alphabetically by TableName, you'd use:
            <xsl:sort select="TableName" /> -->
            <Table>
                <!-- Now, within each `table`, we want to process all those
                `GUID`s with this same corresponding `TableName`. -->
                <xsl:for-each select="key('table', TableName)">
                    <!-- We select "this", since we want to process the matching
                    `GUID`, not just it's children. -->
                    <xsl:apply-templates select="."/>
                </xsl:for-each>
            </Table>
        </xsl:for-each>
    </data>

</xsl:template>
<!-- List up the elements we don't want to copy verbatim into each table row -->
<xsl:variable name="nocopy">
    <item>RowNumber</item>
    <item>TableName</item>
    <!-- Copy in the bits from $kids -->
    <xsl:for-each select="$kids/*">
        <item><xsl:value-of select="name(.)"/></item>
    </xsl:for-each>
</xsl:variable>

<xsl:template match="GUID">
    <TableRow RowNumber="{RowNumber}" TableName="{TableName}">
        <!-- Copy over child data, but _only_ if it's not in `$nocopy` -->
        <xsl:copy-of select="*[not(name() = $nocopy/item)]"/>
    </TableRow>
</xsl:template>

行数
表名
现在,这将生成与所需输出XML功能相同的输出。唯一的区别是元素的顺序——
TableRow
子元素的顺序不同,
Notes
位于
data
的顶部,与
ContractName
ContractNumber
并列,而不是位于
data
的底部

关于输出XML数据格式的说明 将表名作为属性包含在每个表行中似乎有点奇怪。将其作为
元素本身的一个属性会更有意义

类似地,在每一行上都有
RowNumber
属性似乎是多余的。这些信息可以通过查看
<!-- List up the elements we don't want to copy verbatim into each table row -->
<xsl:variable name="nocopy">
    <item>RowNumber</item>
    <item>TableName</item>
    <!-- Copy in the bits from $kids -->
    <xsl:for-each select="$kids/*">
        <item><xsl:value-of select="name(.)"/></item>
    </xsl:for-each>
</xsl:variable>

<xsl:template match="GUID">
    <TableRow RowNumber="{RowNumber}" TableName="{TableName}">
        <!-- Copy over child data, but _only_ if it's not in `$nocopy` -->
        <xsl:copy-of select="*[not(name() = $nocopy/item)]"/>
    </TableRow>
</xsl:template>