使用 xstl 2.0 合并两个 xml 文件时如何保持第一个文件中元素的顺序

How to keep order of elements in first file when merging two xml files using xstl 2.0

我有两个 XML 文件,其中包含 header 部分和预告片。该部分本身有部分 header、部分详细信息和部分预告片。我需要在两个级别合并这两个文件 - 首先在部分级别,然后在部分详细信息级别。我希望我的结果基于第一个文件(Headers,预告片将来自第一个文件)。如果该部分匹配,我需要保留第一个文件中部分详细信息的顺序(没有用于排序的排序键,只有出现顺序)。如果第一个文件中没有节,我需要从第二个文件中添加整个节。

我有 xsl,它给出了结果,但顺序不正确。我需要有关如何订购它们的帮助。我没有尝试密钥查找,因为我不确定如何解释第一个文件中不存在的部分。 当 SectionDetails 匹配时,我需要第一个文件中的记录出现在第二个文件中的记录之前。

我的第一个文件 FileA 在这里

<FileRecord>
    <HeaderRecord>
        <A>FileA</A>
    </HeaderRecord>
    <SectionRecord Subject="Science">
        <SectionHeader>
            <A>FileA</A>
        </SectionHeader>
        <SectionDetails Stream="Physics">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Chemistry">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Biology">
            <A>FileA</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileA</A>
        </SectionTrailer>
    </SectionRecord>
    <SectionRecord Subject="Math">
        <SectionHeader>
            <A>FileA</A>
        </SectionHeader>
        <SectionDetails Stream="Algebra">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Calculus">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Geometry">
            <A>FileA</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileA</A>
        </SectionTrailer>
    </SectionRecord>
    <TrailerRecord>
        <A>FileA</A>
    </TrailerRecord>
</FileRecord>

第二个文件FileB在这里

<FileRecord>
    <HeaderRecord>
        <A>FileB</A>
    </HeaderRecord>
    <SectionRecord Subject="Science">
        <SectionHeader>
            <A>FileB</A>
        </SectionHeader>
        <SectionDetails Stream="Chemistry">
            <A>FileB</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileB</A>
        </SectionTrailer>
    </SectionRecord>
    <SectionRecord Subject="Math">
        <SectionHeader>
            <A>FileB</A>
        </SectionHeader>
        <SectionDetails Stream="Geometry">
            <A>FileB</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileB</A>
        </SectionTrailer>
    </SectionRecord>
    <SectionRecord Subject="History">
        <SectionHeader>
            <A>FileB</A>
        </SectionHeader>
        <SectionDetails Stream="Ancient">
            <A>FileB</A>
        </SectionDetails>
        <SectionDetails Stream="Modern">
            <A>FileB</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileB</A>
        </SectionTrailer>
    </SectionRecord>
    <TrailerRecord>
        <A>FileB</A>
    </TrailerRecord>
</FileRecord>

我正在使用的 xsl 在这里

<xsl:stylesheet version="2.0"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                exclude-result-prefixes="xsd xsi xsl"
>
    <xsl:param name="filebrecs" select="document('FileB.xml')"/>

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

    <xsl:template match="/">
        <xsl:apply-templates select="FileRecord"/>
    </xsl:template>

    <xsl:template match="FileRecord">
        <FileRecord>
            <xsl:apply-templates select="HeaderRecord"/>
            <xsl:for-each-group select="SectionRecord, $filebrecs/FileRecord/SectionRecord" group-by="@Subject">
                <SectionRecord>
                    <xsl:attribute name="Subject"><xsl:value-of select="current-grouping-key()"/> </xsl:attribute>
                    <xsl:apply-templates select="current-group()[1]/SectionHeader"/>
                    <xsl:for-each-group select="current-group()//SectionDetails" group-by="@Stream">
                        <xsl:for-each select="current-group()">
                            <xsl:apply-templates select="."/>
                        </xsl:for-each>
                    </xsl:for-each-group>
                    <xsl:apply-templates select="current-group()[1]/SectionTrailer"/>
                </SectionRecord>
            </xsl:for-each-group>
            <xsl:apply-templates select="TrailerRecord"/>
        </FileRecord>
    </xsl:template>

</xsl:stylesheet>

我期待这样的结果

<FileRecord>
    <HeaderRecord>
        <A>FileA</A>
    </HeaderRecord>
    <SectionRecord Subject="Science">
        <SectionHeader>
            <A>FileA</A>
        </SectionHeader>
        <SectionDetails Stream="Physics">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Chemistry">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Chemistry">
            <A>FileB</A>
        </SectionDetails>
        <SectionDetails Stream="Biology">
            <A>FileA</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileA</A>
        </SectionTrailer>
    </SectionRecord>
    <SectionRecord Subject="Math">
        <SectionHeader>
            <A>FileA</A>
        </SectionHeader>
        <SectionDetails Stream="Algebra">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Calculus">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Geometry">
            <A>FileA</A>
        </SectionDetails>
        <SectionDetails Stream="Geometry">
            <A>FileB</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileA</A>
        </SectionTrailer>
    </SectionRecord>
    <SectionRecord Subject="History">
        <SectionHeader>
            <A>FileB</A>
        </SectionHeader>
        <SectionDetails Stream="Ancient">
            <A>FileB</A>
        </SectionDetails>
        <SectionDetails Stream="Modern">
            <A>FileB</A>
        </SectionDetails>
        <SectionTrailer>
            <A>FileB</A>
        </SectionTrailer>
    </SectionRecord>

    <TrailerRecord>
        <A>FileA</A>
    </TrailerRecord>
</FileRecord>

我得到的实际结果是

<?xml version = '1.0' encoding = 'UTF-8'?>
<FileRecord>
   <HeaderRecord>
        <A>FileA</A>
    </HeaderRecord>
   <SectionRecord Subject="Science">
      <SectionHeader>
            <A>FileA</A>
        </SectionHeader>
      <SectionDetails Stream="Physics">
            <A>FileA</A>
        </SectionDetails>
      <SectionDetails Stream="Chemistry">
            <A>FileB</A>
        </SectionDetails>
      <SectionDetails Stream="Chemistry">
            <A>FileA</A>
        </SectionDetails>
      <SectionDetails Stream="Biology">
            <A>FileA</A>
        </SectionDetails>
      <SectionTrailer>
            <A>FileA</A>
        </SectionTrailer>
   </SectionRecord>
   <SectionRecord Subject="Math">
      <SectionHeader>
            <A>FileA</A>
        </SectionHeader>
      <SectionDetails Stream="Geometry">
            <A>FileB</A>
        </SectionDetails>
      <SectionDetails Stream="Geometry">
            <A>FileA</A>
        </SectionDetails>
      <SectionDetails Stream="Algebra">
            <A>FileA</A>
        </SectionDetails>
      <SectionDetails Stream="Calculus">
            <A>FileA</A>
        </SectionDetails>
      <SectionTrailer>
            <A>FileA</A>
        </SectionTrailer>
   </SectionRecord>
   <SectionRecord Subject="History">
      <SectionHeader>
            <A>FileB</A>
        </SectionHeader>
      <SectionDetails Stream="Ancient">
            <A>FileB</A>
        </SectionDetails>
      <SectionDetails Stream="Modern">
            <A>FileB</A>
        </SectionDetails>
      <SectionTrailer>
            <A>FileB</A>
        </SectionTrailer>
   </SectionRecord>
   <TrailerRecord>
        <A>FileA</A>
    </TrailerRecord>
</FileRecord>

在 Subject=Science 部分,物理、化学和生物的顺序是正确的,但我希望 FileA 记录出现在 FileB 记录之前。在数学部分记录中,几何出现在代数和微积分之前。我希望它按 FileA 的顺序出现(并且 FileA 记录出现在 FileB 记录之前)。为什么它弄乱了数学的顺序而不是科学的顺序?

我也不喜欢使用硬编码数字来访问第一个文件记录 <xsl:apply-templates select="current-group()[1]/SectionHeader"/>

有没有更好的方法呢

我试图重现你的错误,但我的输出符合你想要的结果。

使用FileA.xml作为第一个输入文件:

transform.xslt FileA.xml

FileB.xml 作为 XSLT 中的第二个输入文件:

<xsl:param name="filebrecs" select="document('FileB.xml')"/>

那么您的输出将符合预期。

尝试改变

         <xsl:for-each-group select="current-group()//SectionDetails" group-by="@Stream">
                    <xsl:for-each select="current-group()">
                        <xsl:apply-templates select="."/>
                    </xsl:for-each>
         </xsl:for-each-group>

         <xsl:for-each-group select="for $rec in current-group() return $rec/SectionDetails" group-by="@Stream">
                    <xsl:apply-templates select="current-group()"/>
         </xsl:for-each-group> 

当您处理来自不同文档的节点时,使用 for return 表达式应该保留外部总体的顺序,而如果您使用 current-group()//SectionDetails.

,则它是未定义和不可预测的

至于简化 <xsl:apply-templates select="current-group()[1]/SectionHeader"/>,在 for-each-group 内部,每组中的第一项是上下文项,因此您可以简单地使用 . 而不是 current-group()[1],例如./SectionHeader 当然可以缩短为 SectionHeader<xsl:apply-templates select="SectionHeader"/>

但不确定为什么要使用像 for-each-group 这样的 XSLT 2 结构,并在对其他答案的评论中提到 Xalan,它是 XSLT 1 处理器,不支持 for-each-group