创建 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>