Вставить узел в другой XML, создать новые элементы (или обновить существующие) и изменить порядок результирующего документа

Ну, разместив мой первый вопрос, даже если я усердно использую сайт. Я пытался найти решение для этого в течение последних двух дней без успеха. Использование некоторых ответов на подобные вопросы на этом сайте (этой, этой, этой, этой и многие, многие другие) я смог добиться некоторого прогресса, но полного (и правильного! решение все еще ускользает от меня.

у меня есть существующий XML (file1.в XML) , что я должен обновить на основе другого, который я генерирую (file2.в XML): содержание file2 должен быть включен на file1 соблюдая некоторые правила, которые я изложу позже, (содержимое файлов было упрощено, чтобы показать только соответствующие элементы):

file1.в XML

<?xml version="1.0" encoding="UTF-8"?>
<list>
    <decade lastyear="2012" firstyear="2011">
        <year value="2012">
            <issue year="2012"  number="242" />
            <issue year="2012"  number="241" />
            <issue year="2012"  number="240" />
        </year>
        <year value="2011">
            <issue year="2011"  number="238" />
            <issue year="2011"  number="237" />
            <issue year="2011"  number="236" />
            <issue year="2011"  number="235" />
        </year>
    </decade>
    <decade lastyear="2010" firstyear="2001">
        <year value="2010">
            <issue year="2010"  number="234" />
            <issue year="2010"  number="233" />
            <issue year="2010"  number="232" />
            <issue year="2010"  number="231" />
            <issue year="2010"  number="230" />
        </year>
        <year value="2009">
            <issue year="2009"  number="229" />
            <issue year="2009"  number="228" />
            <issue year="2009"  number="227" />
            <issue year="2009"  number="226" />
            <issue year="2009"  number="225" />
        </year>
           ...
    </decade>
 </list>

file2.в XML

<?xml version="1.0" encoding="UTF-8"?>
<issue year="2013" number="245" />
...

как уже было сказано, содержание file2 необходимо вставить в file1 С некоторыми правилами, которые следует соблюдать:

  • если Год выпуска не существует в file1 (т. е. при вставке первого выпуска года), он должен быть создан (уже сделан)
  • новая вопрос должны быть помещены под соответствующий год (уже сделано)
  • десятилетия должен быть обновлен, чтобы отразить последний вставленный год (возникли проблемы с этим !)
  • в вопрос элемент должен быть упорядочен в порядке убывания по годам и
  • если Год выпуска относится к новому десятилетию, этот должен быть создан вместе с соответствующим дочерним годом и выпуском (- АМИ)
  • в итоговом документе все элементы должны быть упорядочены в порядке убывания: десятилетие (последний год), год (значение) и выпуск (год и номер)

Я использую Saxon-HE 9.4.0.6 и xsl, который я сделал до сих пор этот:

xsl-код

<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output method="xml" omit-xml-declaration="yes" indent="no" encoding="UTF-8"/>

    <xsl:variable name="up" select="document('../test/ExcelStory/file2.xml')"/>
    <xsl:variable name="year" select="$up/issue/@year" />

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

    <xsl:template match="decade" >
        <xsl:copy>
            <xsl:apply-templates select="* | @*"/>
            <xsl:choose>
                <xsl:when test="year[1]/@value lt $year">
                    <year value="{$year}"/>
                </xsl:when>
            </xsl:choose>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="year[@value=$year]">
        <xsl:copy>
            <xsl:apply-templates select="* | @*"/>
            <xsl:apply-templates select="$up/*" />
        </xsl:copy>
    </xsl:template>    
</xsl:stylesheet>

эта таблица стилей предполагает содержание на file1.в XML уже заказано при чтении (это так).

мне интересно, должен ли я сделать более одного прохода, используя "режим", чтобы сначала создать десятилетие в соответствии с годом (если необходимо), а затем вставить год в правильное десятилетие (на втором проходе??), после этого вставить вопросы на правильный год (третий проход??) и, наконец, переупорядочить все элементы (еще один проход??) или если вся необходимая обработка может быть выполнена более эффективно (один или два прохода). Г-н Майкл Кей предложил где-то еще использовать xsl:for-each для такого рода обработки, но я не знаю, Может ли он поместиться лучше (проще?) в данном случае.

даже если этот вопрос может показаться похожим на некоторые другие в stackoverflow, я думаю, что есть некоторая дополнительная сложность, которая делает его достойным чтения (и может быть ответ, я надеюсь!).

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

1 ответов


что бы я сделал вместо того, чтобы пытаться добавить новый issue(s) должен объединить все issueS из обоих файлов, а затем воссоздать структуру.

это может не работать для вашего фактического использования случае, потому что вы сказали:

(содержимое файлов было упрощено, чтобы показать только соответствующие elements)

но, надеюсь, это даст вам другую перспективу и/или отправную точку.

вы, вероятно, захотите добавить identity transform and replace xsl:copy-of и xsl:perform-sort С xsl:apply-templates. Вам также необходимо обновить xsl:param для ссылки на внешний файл.

ввод XML (слегка изменен, чтобы добавить больше лет и изменить нумерацию для тестирования)

<list>
    <decade lastyear="2012" firstyear="2011">
        <year value="2012">
            <issue year="2012"  number="242" />
            <issue year="2012"  number="241" />
            <issue year="2012"  number="240" />
        </year>
        <year value="2011">
            <issue year="2011"  number="238" />
            <issue year="2011"  number="237" />
            <issue year="2011"  number="236" />
            <issue year="2011"  number="235" />
        </year>
    </decade>
    <decade lastyear="2010" firstyear="2001">
        <year value="2010">
            <issue year="2010"  number="234" />
            <issue year="2010"  number="232" />
            <issue year="2010"  number="233" />
            <issue year="2010"  number="231" />
            <issue year="2010"  number="230" />
        </year>
        <year value="2009">
            <issue year="2009"  number="229" />
            <issue year="2009"  number="228" />
            <issue year="2009"  number="227" />
            <issue year="2009"  number="226" />
            <issue year="2009"  number="225" />
        </year>
        <year value="2001">
            <issue year="2001"  number="123" />
        </year>
    </decade>
</list>

XSLT 2.0

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!--This can be changed to point to an external XML file.-->
    <xsl:param name="up">
        <issue year="2013" number="245" />
        <issue year="2002" number="135" />
        <issue year="2011" number="239" />
    </xsl:param>

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:for-each-group select="($up/issue|*/*/issue)" group-by="floor((number(@year) - 1) div 10)">
                <xsl:sort select="@year" data-type="number" order="descending"/>
                <decade lastyear="{max(current-group()/@year)}" firstyear="{min(current-group()/@year)}">
                    <xsl:for-each-group select="current-group()" group-by="@year">
                        <xsl:sort select="current-grouping-key()" data-type="number" order="descending"/>                   
                        <year value="{current-grouping-key()}">
                            <xsl:perform-sort select="current-group()">
                                <xsl:sort select="@number" data-type="number" order="descending"/>
                            </xsl:perform-sort>
                        </year>
                    </xsl:for-each-group>
                </decade>
            </xsl:for-each-group>           
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

вывод XML

<list>
   <decade lastyear="2013" firstyear="2011">
      <year value="2013">
         <issue year="2013" number="245"/>
      </year>
      <year value="2012">
         <issue year="2012" number="242"/>
         <issue year="2012" number="241"/>
         <issue year="2012" number="240"/>
      </year>
      <year value="2011">
         <issue year="2011" number="239"/>
         <issue year="2011" number="238"/>
         <issue year="2011" number="237"/>
         <issue year="2011" number="236"/>
         <issue year="2011" number="235"/>
      </year>
   </decade>
   <decade lastyear="2010" firstyear="2001">
      <year value="2010">
         <issue year="2010" number="234"/>
         <issue year="2010" number="233"/>
         <issue year="2010" number="232"/>
         <issue year="2010" number="231"/>
         <issue year="2010" number="230"/>
      </year>
      <year value="2009">
         <issue year="2009" number="229"/>
         <issue year="2009" number="228"/>
         <issue year="2009" number="227"/>
         <issue year="2009" number="226"/>
         <issue year="2009" number="225"/>
      </year>
      <year value="2002">
         <issue year="2002" number="135"/>
      </year>
      <year value="2001">
         <issue year="2001" number="123"/>
      </year>
   </decade>
</list>