Как сохранить порядок элементов в первом файле при объединении двух файлов XML с использованием xstl 2.0 - PullRequest
0 голосов
/ 03 мая 2019

У меня есть два XML-файла с заголовком, разделом и трейлером.Сам раздел имеет заголовок раздела, подробности раздела и раздел трейлера.Мне нужно объединить два файла на двух уровнях - сначала на уровне раздела, а затем на уровне детализации раздела.Я хочу, чтобы мои результаты основывались на первом файле (заголовки и трейлеры будут исходить из первого файла).Если раздел совпадает, мне нужно сохранить порядок деталей раздела из первого файла (для сортировки нет ключа сортировки, только порядок вхождения).Если в первом файле нет раздела, мне нужно добавить весь раздел из второго файла.

У меня есть xsl, который дает мне результаты, но порядок не правильный.Мне нужна помощь о том, как их заказать.Я не пробовал поиск ключей, так как не знал, как учесть разделы, которых нет в первом файле.Когда MatchDetails совпадает, мне нужно, чтобы записи из первого файла появлялись перед записями из второго файла.

Мой первый файл 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 голосов
/ 04 мая 2019

Попробуйте изменить

         <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"/>

Однако не уверен, почему вы используете конструкции XSLT 2, такие как for-each-group, и в комментарии к другому ответу упомяните Xalan, который, будучи процессором XSLT 1, не поддерживает for-each-group.

0 голосов
/ 03 мая 2019

Я пытался воспроизвести вашу ошибку, но мой вывод соответствовал желаемому результату.

Используйте FileA.xml в качестве первого входного файла:

transform.xslt FileA.xml

и FileB.xml в качествевторой входной файл в вашем XSLT:

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

Тогда ваш вывод будет таким, как ожидалось.

...