创建 header/details XML 时如何 sort/select 在 XSLT 中区分
How to sort/select distinct in XSLT when creating a header/details XML
我有一个包含 header 记录和一对多详细记录的文件。我需要创建相同的 header 记录,以及给定保单编号的最后一条详细记录。我如何编写 XSLT 来排序和 select 第一条记录?
我尝试在 XSLT 中的 "for-each" 语句之间放置一个硬编码值,以查看代码中断的位置。放在第二个 "for-each" 之后的任何内容都不会显示。我到处搜索,看看是否有人可以处理包含多个细节的 header,但我找不到这个问题的答案。
这是我收到的(经过简化 - 有更多字段):
<ns0:Root xmlns:ns0="http://CORE.BizTalk.Applications.Schemas.PolicyFF">
<Header>
<RecordType>RecordType</RecordType>
<RecordID>RecordID</RecordID>
<PolicyNumber>PolicyNumber</PolicyNumber>
</Header>
<Detail>
<RecordType>POL</RecordType>
<RecordID>1</RecordID>
<PolicyNumber>ABC 0000018 00</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>2</RecordID>
<PolicyNumber>DEF0000019</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>DEF0000019</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>ABC 0000018 00</PolicyNumber>
</Detail>
</ns0:Root>
输出具有相同的布局,但我只想看到这个,因为 RecordID 是两个策略的最后一个 RecordID(按每个策略的 RecordID 降序排列):
<ns0:Root xmlns:ns0="http://CORE.BizTalk.Applications.Schemas.PolicyFF">
<Header>
<RecordType>RecordType</RecordType>
<RecordID>RecordID</RecordID>
<PolicyNumber>PolicyNumber</PolicyNumber>
</Header>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>DEF0000019</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>ABC 0000018 00</PolicyNumber>
</Detail>
</ns0:Root>
这是我正在使用的 XSLT:
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var" version="1.0" xmlns:ns0="http://CORE.BizTalk.Applications.Schemas.PolicyFF">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:key name="policy" match="Root/Detail" use="PolicyNumber" />
<xsl:template match="/">
<xsl:apply-templates select="/ns0:Root" />
</xsl:template>
<xsl:template match="/ns0:Root">
<ns0:Root>
<Header>
<RecordType>
<xsl:value-of select="Header/RecordType/text()" />
</RecordType>
<RecordID>
<xsl:value-of select="Header/RecordID/text()" />
</RecordID>
<PolicyNumber>
<xsl:value-of select="Header/PolicyNumber/text()" />
</PolicyNumber>
</Header>
<xsl:for-each select="Detail">
<xsl:for-each select="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort order="descending" select="RecordID" data-type="text"/>
<xsl:if test="position() = 1">
<Detail>
<xsl:copy-of select="./*" />
</Detail>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</ns0:Root>
</xsl:template>
</xsl:stylesheet>
我目前只获取带有上述 XSLT 的 header 记录。
@Martin-Honnen:我需要按RecordID降序排序。我采用了您的第一个和第二个 XSLT 并将其变成一个,但我仍然只获得 header 记录。建议?
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var" version="1.0" xmlns:ns0="http://CORE.BizTalk.Applications.PPPL.TrisuraReports.Schemas.PolicyFF">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:key name="policy" match="Root/Detail" use="PolicyNumber" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[1]))]"/>
<xsl:template match="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="text" order="descending"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
在 XSLT 1 中,如果 "with the last detail record" 表示输入中的最后一个,我将简单地使用身份转换模板加上一个模板来防止复制任何 Detail
不是其最后定义的组通过关键:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="policy" match="Detail" use="PolicyNumber" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[last()]))]"/>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/3NSSEuP
如果您需要按 RecordID
排序,那么在组的第一项或最后一项的一个模板中,您可以对组中的所有项目进行排序,并按降序输出最后一项:
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[1]))]"/>
<xsl:template match="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="number" order="descending"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
https://xsltfiddle.liberty-development.net/3NSSEuP/1 是完整的在线样本并且有
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="policy" match="Detail" use="PolicyNumber" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[1]))]"/>
<xsl:template match="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="number" order="descending"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我会这样做:
XSLT 1.0
<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="policy" match="Detail" use="PolicyNumber" />
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="Header"/>
<!-- for each distinct policy -->
<xsl:for-each select="Detail[count(. | key('policy', PolicyNumber)[1]) = 1]">
<!-- sort current group -->
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="number" order="descending"/>
<!-- return the record with highest RecordID -->
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我有一个包含 header 记录和一对多详细记录的文件。我需要创建相同的 header 记录,以及给定保单编号的最后一条详细记录。我如何编写 XSLT 来排序和 select 第一条记录?
我尝试在 XSLT 中的 "for-each" 语句之间放置一个硬编码值,以查看代码中断的位置。放在第二个 "for-each" 之后的任何内容都不会显示。我到处搜索,看看是否有人可以处理包含多个细节的 header,但我找不到这个问题的答案。
这是我收到的(经过简化 - 有更多字段):
<ns0:Root xmlns:ns0="http://CORE.BizTalk.Applications.Schemas.PolicyFF">
<Header>
<RecordType>RecordType</RecordType>
<RecordID>RecordID</RecordID>
<PolicyNumber>PolicyNumber</PolicyNumber>
</Header>
<Detail>
<RecordType>POL</RecordType>
<RecordID>1</RecordID>
<PolicyNumber>ABC 0000018 00</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>2</RecordID>
<PolicyNumber>DEF0000019</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>DEF0000019</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>ABC 0000018 00</PolicyNumber>
</Detail>
</ns0:Root>
输出具有相同的布局,但我只想看到这个,因为 RecordID 是两个策略的最后一个 RecordID(按每个策略的 RecordID 降序排列):
<ns0:Root xmlns:ns0="http://CORE.BizTalk.Applications.Schemas.PolicyFF">
<Header>
<RecordType>RecordType</RecordType>
<RecordID>RecordID</RecordID>
<PolicyNumber>PolicyNumber</PolicyNumber>
</Header>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>DEF0000019</PolicyNumber>
</Detail>
<Detail>
<RecordType>POL</RecordType>
<RecordID>3</RecordID>
<PolicyNumber>ABC 0000018 00</PolicyNumber>
</Detail>
</ns0:Root>
这是我正在使用的 XSLT:
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var" version="1.0" xmlns:ns0="http://CORE.BizTalk.Applications.Schemas.PolicyFF">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:key name="policy" match="Root/Detail" use="PolicyNumber" />
<xsl:template match="/">
<xsl:apply-templates select="/ns0:Root" />
</xsl:template>
<xsl:template match="/ns0:Root">
<ns0:Root>
<Header>
<RecordType>
<xsl:value-of select="Header/RecordType/text()" />
</RecordType>
<RecordID>
<xsl:value-of select="Header/RecordID/text()" />
</RecordID>
<PolicyNumber>
<xsl:value-of select="Header/PolicyNumber/text()" />
</PolicyNumber>
</Header>
<xsl:for-each select="Detail">
<xsl:for-each select="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort order="descending" select="RecordID" data-type="text"/>
<xsl:if test="position() = 1">
<Detail>
<xsl:copy-of select="./*" />
</Detail>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</ns0:Root>
</xsl:template>
</xsl:stylesheet>
我目前只获取带有上述 XSLT 的 header 记录。
@Martin-Honnen:我需要按RecordID降序排序。我采用了您的第一个和第二个 XSLT 并将其变成一个,但我仍然只获得 header 记录。建议?
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var" version="1.0" xmlns:ns0="http://CORE.BizTalk.Applications.PPPL.TrisuraReports.Schemas.PolicyFF">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:key name="policy" match="Root/Detail" use="PolicyNumber" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[1]))]"/>
<xsl:template match="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="text" order="descending"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
在 XSLT 1 中,如果 "with the last detail record" 表示输入中的最后一个,我将简单地使用身份转换模板加上一个模板来防止复制任何 Detail
不是其最后定义的组通过关键:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="policy" match="Detail" use="PolicyNumber" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[last()]))]"/>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/3NSSEuP
如果您需要按 RecordID
排序,那么在组的第一项或最后一项的一个模板中,您可以对组中的所有项目进行排序,并按降序输出最后一项:
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[1]))]"/>
<xsl:template match="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="number" order="descending"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
https://xsltfiddle.liberty-development.net/3NSSEuP/1 是完整的在线样本并且有
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="policy" match="Detail" use="PolicyNumber" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail[not(generate-id() = generate-id(key('policy', PolicyNumber)[1]))]"/>
<xsl:template match="Detail[generate-id() = generate-id(key('policy', PolicyNumber)[1])]">
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="number" order="descending"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我会这样做:
XSLT 1.0
<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="policy" match="Detail" use="PolicyNumber" />
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="Header"/>
<!-- for each distinct policy -->
<xsl:for-each select="Detail[count(. | key('policy', PolicyNumber)[1]) = 1]">
<!-- sort current group -->
<xsl:for-each select="key('policy', PolicyNumber)">
<xsl:sort select="RecordID" data-type="number" order="descending"/>
<!-- return the record with highest RecordID -->
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>