2017-02-13 6 views
-1

Примечание: это не дубликат Merge 2 XML files based on attribute values using XSLT? , но расширение для него. Учитывая следующие входные файлыОбъединить 2 файла XML (включая атрибуты узлов) на основе значений атрибутов с использованием XSLT

file1.xml

<config> 
<state version="10"> 
    <root value="100" group="5"> 
    <leaf number = "2"/> 
    </root> 
    <root value="101" group="6" overrideAttr="oldval"> 
    <leaf number = "3"/> 
    </root> 
</state> 
</config> 

file2.xml

<config> 
<state version="10"> 
    <root value="100" group="5"> 
    <leaf number = "6"/> 
    </root> 
    <root value="101" group="6" overrideAttr="newval" addtionalAttr="hello"> 
    <leaf number = "4"/> 
    </root> 
</state> 
</config> 

Я хотел бы иметь эту output.xml

<config> 
<state version="10"> 
    <root value="100" group="5"> 
    <leaf number = "2"/> 
    <leaf number = "6"/> 
    </root> 
    <root value="101" group="6" overrideAttr="newval" addtionalAttr="hello"> 
    <leaf number = "3"/> 
    <leaf number = "4"/> 
    </root> 
</state> 
</config> 

Нужные расширений

  • атрибуты (например, overrideAttr) на «тот же узел» (например, корневой элемент со значением = «101» и группа = «6») должны быть перезаписаны
  • новых атрибутов (например, addtionalAttr) должна быть добавлена ​​

это может быть достигнуты по xsl?

+1

** 1. ** Что определяет «тот же» узел? - ** 2. ** Укажите, используете ли XSLT 1.0 или 2.0. –

+0

> 1.Что определяет «тот же» узел? см. «Решение» вышеупомянутой темы > 2. Укажите, используете ли XSLT 1.0 или 2.0 независимо от javax.xml.transform.Поддержка TransformerFactory – Clemens

ответ

1

В ответ, на который вы ссылаетесь, есть xsl:apply-templates, который копирует дочерние элементы из второго файла.

<xsl:apply-templates 
    select="document('file2.xml') 
      /config/state[@version = current()/../@version] 
       /root[@value = current()/@value and 
         @group = current()/@group]/*" /> 

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

<xsl:apply-templates 
    select="document('file2.xml') 
      /config/state[@version = current()/../@version] 
       /root[@value = current()/@value and 
         @group = current()/@group]/@*" /> 

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

Кроме того, вы можете использовать переменную, чтобы избежать повторения выражения xpath.

Попробуйте XSLT ...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes" omit-xml-declaration="yes"/> 

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

    <xsl:template match="root"> 
     <xsl:variable name="file2root" select="document('file2.xml') 
       /config/state[@version = current()/../@version] 
        /root[@value = current()/@value and 
          @group = current()/@group]" /> 
    <xsl:copy> 
     <xsl:apply-templates select="@*" /> 
     <xsl:apply-templates select="$file2root/@*" /> 
      <xsl:apply-templates select="node()" /> 
     <xsl:apply-templates select="$file2root/*" /> 
    </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Обратите внимание, что это имеет преимущество в том, что «Добавление атрибута к элементу замещает любой существующий атрибут этого элемента с тем же расширенным именем». (См https://www.w3.org/TR/xslt#creating-attributes)

0

Если вы хотите, чтобы расширить это решение, то вы можете сделать это следующим образом, изменить этот шаблон для root элементов (http://xsltransform.net/gWEamLR/1) в

 <xsl:template match="root"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"/> 
     <xsl:apply-templates select="$doc2 
       /config/state[@version = current()/../@version] 
        /root[@value = current()/@value and 
          @group = current()/@group]/@*" /> 
     <xsl:apply-templates select="node()"/> 
     <xsl:apply-templates select=" 
     $doc2 
       /config/state[@version = current()/../@version] 
        /root[@value = current()/@value and 
          @group = current()/@group]/*" /> 
    </xsl:copy> 
    </xsl:template> 

и убедитесь, что вы определяете <xsl:param name="doc2" select="document('file2.xml')"/>.

Там может быть более эффективные способы с помощью клавиш или группировки делают, что слияние, и, конечно, в XSLT 3.0 мы теперь имеем xsl:merge

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="xs math" 
    version="3.0"> 

    <xsl:output indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="/" name="main"> 
     <config> 
      <xsl:merge> 
       <xsl:merge-source select="doc('file1.xml')/config/state"> 
        <xsl:merge-key select="@version"/> 
       </xsl:merge-source> 
       <xsl:merge-source select="doc('file2.xml')/config/state"> 
        <xsl:merge-key select="@version"/> 
       </xsl:merge-source> 
       <xsl:merge-action> 
        <xsl:copy> 
         <xsl:copy-of select="@*"/> 
         <xsl:merge> 
          <xsl:merge-source select="current-merge-group()[1]/root"> 
           <xsl:merge-key select="@value"/> 
           <xsl:merge-key select="@group"/> 
          </xsl:merge-source> 
          <xsl:merge-source select="current-merge-group()[2]/root"> 
           <xsl:merge-key select="@value"/> 
           <xsl:merge-key select="@group"/> 
          </xsl:merge-source> 
          <xsl:merge-action> 
           <xsl:copy> 
            <xsl:copy-of select="current-merge-group()[2]/@*"/> 
            <xsl:copy-of select="current-merge-group()/node()"/> 
           </xsl:copy> 
          </xsl:merge-action> 
         </xsl:merge> 
        </xsl:copy> 
       </xsl:merge-action> 
      </xsl:merge> 
     </config> 
    </xsl:template> 

</xsl:stylesheet> 
Смежные вопросы