使用 xslt 3 对两个 xml 文件之间符合特定条件的数据进行排序然后合并

sort and then merge only data that match certain criteria between two xml files, using xslt 3

给定以下 xml 个文件:

  <?xml version="1.0" encoding="UTF-8"?>
    <TABLE NAME="File_A">
        <DATA RECORDS="2">
            <RECORD ID="1">
                <PID>2</PID>
                <ENTRYDATE>4/12/2002</ENTRYDATE>
                <VERSIONID>28/12/2016</VERSIONID>
                <REGDATE>8/9/1986</REGDATE>
                <EXPIREDDATE>12/2/2010</EXPIREDDATE>
                <TOTALCHARGES>25</TOTALCHARGES>
                <DESCRIPTION>J 19/7/01</DESCRIPTION>
                        </RECORD>
            <RECORD ID="2">
                <PID>3</PID>
                <ENTRYDATE>4/12/2002</ENTRYDATE>
                <VERSIONID>5/12/2018</VERSIONID>
                <REGDATE>30/6/1984</REGDATE>
                <EXPIREDDATE>23/4/2018</EXPIREDDATE>
                <TOTALCHARGES>544</TOTALCHARGES>
                <DESCRIPTION>G 24-1-01</DESCRIPTION>
                        </RECORD>
        </DATA>
    </TABLE>

    File B:
<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="FILE_B">
    <DATA RECORDS="2">
        <RECORD ID="1">
            <PID>2</PID>
            <SN>2</SN>
            <OI>hjg</OI>
            <TEMPSTARTDATE>19/3/2003</TEMPSTARTDATE>
            <TEMPENDDATE>1/4/2015</TEMPENDDATE>
            <PCOD>0</PCOD>
            <NOTICEBY>1</NOTICEBY>
            <MAIN>8453304</MAIN>
        </RECORD>
        <RECORD ID="38">
            <PID>36</PID>
            <SN>2</SN>
            <OI>df</OI>
            <TEMPSTARTDATE>8/4/2003</TEMPSTARTDATE>
            <TEMPENDDATE>8/4/2004</TEMPENDDATE>
            <PCOD>0</PCOD>
            <CITY>PK</CITY>
            <NOTICEBY>0</NOTICEBY>
            <MAIN>697601</MAIN>
        </RECORD>
    </DATA>
</TABLE>

对于文件A的每条记录(PID),我们检查文件B中是否有相同PID(编号)的记录。如果匹配, 然后我尝试从文件 B 中的匹配记录中获取特定元素信息(比如 NOTICEBY 和 MAIN 元素,如果它们存在),并将其放入我们在文件 A 中调查的源记录的 DESCRIPTION 元素中。如果DESCRIPTION 元素在记录中不存在,我们创建一个。

我们还需要在合并之前对 PID 进行排序,因为在我的一项努力中我得到了一个错误: 在第 45 行执行 XSLT 时出错:源 saxon-merge-source-37386217 的合并输入未根据合并键排序,在键值处检测到:["36"]

https://xsltfiddle.liberty-development.net/6r5Gh4c/4 到目前为止的代码: https://xsltfiddle.liberty-development.net/6r5Gh4c/3

期望的输出:

<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="File_A">
    <DATA RECORDS="2">
        <RECORD ID="1">
            <PID>2</PID>
            <ENTRYDATE>4/12/2002</ENTRYDATE>
            <VERSIONID>28/12/2016</VERSIONID>
            <REGDATE>8/9/1986</REGDATE>
            <EXPIREDDATE>12/2/2010</EXPIREDDATE>
            <TOTALCHARGES>25</TOTALCHARGES>
            <DESCRIPTION>J 19/7/01  - info taken from file B - NOTICEBY: 1 - MAIN: 8453304</DESCRIPTION>
                    </RECORD>
        <RECORD ID="2">
            <PID>3</PID>
            <ENTRYDATE>4/12/2002</ENTRYDATE>
            <VERSIONID>5/12/2018</VERSIONID>
            <REGDATE>30/6/1984</REGDATE>
            <EXPIREDDATE>23/4/2018</EXPIREDDATE>
            <TOTALCHARGES>544</TOTALCHARGES>
            <DESCRIPTION>G 24-1-01</DESCRIPTION>
                    </RECORD>
    </DATA>
</TABLE>

如果要合并的项目未按您要使用的合并键排序,您需要确保在相关合并源上使用 sort-before-merge="yes"

至于任务,如果文档 "A" 中的项目作为起点,那么我将使用该文档作为主要输入文档,并将文档 "B" 作为参数 xsl:param name="doc-B"(在示例中内联,但当然可以从文件或一般的 URI 加载 <xsl:param name="doc-B" select="doc('fileB.xml')"/>

您似乎有更多级别的元素,因此不应在匹配根元素的模板中进行合并,而应在匹配 DATA 元素的模板中进行合并。

确保您只从一个源获取数据以及从另一个源获取额外数据的一种方法是命名合并源并将该名称与 current-merge-group 一起使用,在 xsl:if 中或作为我在 xsl:copy:

select 属性中尝试了以下
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    expand-text="yes"
    version="3.0">

  <xsl:param name="doc-B">
<TABLE NAME="FILE_B">
    <DATA RECORDS="2">
        <RECORD ID="1">
            <PID>2</PID>
            <SN>2</SN>
            <OI>hjg</OI>
            <TEMPSTARTDATE>19/3/2003</TEMPSTARTDATE>
            <TEMPENDDATE>1/4/2015</TEMPENDDATE>
            <PCOD>0</PCOD>
            <NOTICEBY>1</NOTICEBY>
            <MAIN>8453304</MAIN>
        </RECORD>
        <RECORD ID="38">
            <PID>36</PID>
            <SN>2</SN>
            <OI>df</OI>
            <TEMPSTARTDATE>8/4/2003</TEMPSTARTDATE>
            <TEMPENDDATE>8/4/2004</TEMPENDDATE>
            <PCOD>0</PCOD>
            <CITY>PK</CITY>
            <NOTICEBY>0</NOTICEBY>
            <MAIN>697601</MAIN>
        </RECORD>
    </DATA>
</TABLE>
  </xsl:param>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="DATA[@RECORDS]">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:merge>
            <xsl:merge-source name="A" for-each-item="." select="RECORD" sort-before-merge="yes">
                <xsl:merge-key select="PID"/>
            </xsl:merge-source>
            <xsl:merge-source name="B" for-each-item="$doc-B" select="//RECORD" sort-before-merge="yes">
                <xsl:merge-key select="PID"/>
            </xsl:merge-source>
            <xsl:merge-action>
                <xsl:copy select="current-merge-group('A')">
                    <xsl:apply-templates select="@*, *">
                        <xsl:with-param name="merge-data" 
                          select="let $data := string-join(current-merge-group()[2]/(NOTICEBY, MAIN)!(name() || ': ' || .), ' - ')
                                  return if (description)
                                         then ' - info taken from file ' || document-uri(root(current-merge-group()[2])) || $data
                                         else $data"/>
                    </xsl:apply-templates>
                </xsl:copy>
            </xsl:merge-action>
        </xsl:merge>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="RECORD/DESCRIPTION">
      <xsl:param name="merge-data"/>
      <xsl:copy>{.}{$merge-data}</xsl:copy>
  </xsl:template>

  <xsl:template match="RECORD[not(DESCRIPTION)]/*[last()]">
      <xsl:param name="merge-data"/>
      <xsl:next-match/>
      <DESCRIPTION>{$merge-data}</DESCRIPTION>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6r5Gh4c/5