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

时间:2019-05-03 20:24:52

标签: xslt-2.0 xslt-grouping

我有两个XML文件,它们有一个标题,一个部分和一个结尾。该部分本身具有部分标题,部分详细信息和部分预告片。我需要在两个级别上合并两个文件-首先在部分级别,然后在部分详细信息级别。我希望我的结果基于第一个文件(标题和结尾将来自第一个文件)。如果节匹配,则需要保留第一个文件中节详细信息的排序(没有排序键,仅是出现的顺序)。如果第一个文件中没有该节,则需要从第二个文件中添加整个节。

我有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>

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

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

有更好的方法吗?

2 个答案:

答案 0 :(得分:0)

我试图重现您的错误,但我的输出与您想要的结果相符。

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

transform.xslt FileA.xml

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

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

然后您的输出将如预期的那样。

答案 1 :(得分:0)

尝试更改

         <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