Как объединить два XML-файла с соответствующим узлом - PullRequest
7 голосов
/ 16 июня 2011

Мне нужно найти способ объединить два XML-файла, когда у них есть соответствующий узел. Из того, что я понял, это можно сделать с помощью множества разных языков ... Есть ли способ сделать это на PHP или AJAX? Из других постов на SO я вижу решения XSLT .. которые я действительно не получаю. Это лучший / предпочтительный метод? Если да, знаете какие-нибудь полезные учебники по XSLT?

Например, XML-1 выглядит так:

<FOO>
    </A>
    </B>
    </C>
    </D>
</FOO>

и XML-2:

<FOO>    
    </B>
    </E>
</FOO>

Каков наилучший подход для проверки, где <B>==<B> затем добавить <E>

Обновление

Ну, я не могу заставить это работать с моим гипотетическим примером и подумал, что обновлю то, что я действительно делаю, чтобы посмотреть, сможет ли кто-нибудь помочь мне понять это. Я попробовал методы из ниже и другие, которые я нашел на SO, но безуспешно.

Реальная схема похожа на:

file1.xml

<?xml version="1.0"?>
<DATA>
  <ITEM>
    <PRODUCT_TYPE>simple</PRODUCT_TYPE>
    <STYLE_COLOR>1524740007</STYLE_COLOR>
    <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
    <CLASS_NAME>FOOTWEAR</CLASS_NAME>
    <STATUS>Disabled</STATUS>
  </ITEM>
 ...
</DATA>

file2.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="merge.xsl" ?>
<DATA>
  <ITEM>
    <STYLE_COLOR>1524740007</STYLE_COLOR>
    <NEXT_ARRIVAL>2011-08-05</NEXT_ARRIVAL>
  </ITEM>
  ....
</DATA>

Что мне нужно выяснить, так это создать новый XML-файл, который объединит эти узлы с идентичным SYTLE_COLOR и будет выглядеть так:

<DATA>
  <ITEM>
    <PRODUCT_TYPE>simple</PRODUCT_TYPE>
    <STYLE_COLOR>1524740007</STYLE_COLOR>
    <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
    <CLASS_NAME>FOOTWEAR</CLASS_NAME>
    <NEXT_ARRIVAL>2011-08-05</NEXT_ARRIVAL>
    <STATUS>Disabled</STATUS>
  </ITEM>

Я попытался создать файл merge.xsl, который выглядит следующим образом:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes" />
  <xsl:output indent="yes"/>
  <xsl:variable name="with" select="'file-2.xml'" />
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="scene">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
      <xsl:variable name="info" select="document($with)/DATA/ITEM[STYLE_COLOR=current()/STYLE_COLOR]/." />
      <xsl:for-each select="$info/*">
        <xsl:if test="name()!='STYLE_COLOR'">
          <xsl:copy-of select="." />
        </xsl:if>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:transform>

Я также попытался выполнить слияние следующим образом:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:variable name="input2" select="document('file-2.xml')/DATA/ITEM"/>
    <xsl:template match="STYLE_COLOR">
        <xsl:copy>
            <xsl:apply-templates select="*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*">
        <xsl:choose>
            <xsl:when test="$input2/*[name()=name(current())]">
                <xsl:copy-of select="$input2/*"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet> 

Ни один из этих методов не работает ... извините, XSLT очень нов для меня, поэтому я не уверен в том, что делаю, и был бы очень признателен за некоторую руку, держащую этот.

Ответы [ 3 ]

5 голосов
/ 16 июня 2011

Это оригинальное преобразование, слегка измененное для адаптации к новым требованиям. Слияние выполняется путем проверки элементов file2.xml . Для текущего ПУНКТА в file1 дочерний ПУНКТ в file2 будет объединен только , если его нет в file1 .


[XSLT 1.0]

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

    <xsl:variable name="input2" select="document('test_input2.xml')/DATA"/>

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

    <xsl:template match="ITEM">
        <xsl:variable name="item" select="
            $input2/ITEM[STYLE_COLOR=current()/STYLE_COLOR]"/>
        <xsl:variable name="ITEM" select="."/>

        <xsl:if test="$item">
            <xsl:copy>

                <xsl:for-each select="$item/*">
                    <xsl:if test="count($ITEM/*[name()=name(current())])=0">
                        <xsl:copy-of select="." />
                    </xsl:if>
                </xsl:for-each>

                <xsl:apply-templates select="*"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet> 

Применяется для этого input1.xml:

<DATA>
  <ITEM>
    <PRODUCT_TYPE>simple</PRODUCT_TYPE>
    <STYLE_COLOR>1524740007</STYLE_COLOR>
    <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
    <CLASS_NAME>FOOTWEAR</CLASS_NAME>
    <STATUS>Disabled</STATUS>
  </ITEM>
  <ITEM>
    <PRODUCT_TYPE>simple</PRODUCT_TYPE>
    <STYLE_COLOR>1524740008</STYLE_COLOR>
    <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
    <CLASS_NAME>FOOTWEAR</CLASS_NAME>
    <STATUS>Disabled</STATUS>
  </ITEM>
  <ITEM>
    <PRODUCT_TYPE>simple</PRODUCT_TYPE>
    <STYLE_COLOR>777</STYLE_COLOR>
    <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
    <CLASS_NAME>FOOTWEAR</CLASS_NAME>
    <STATUS>Disabled</STATUS>
  </ITEM>
</DATA>

и input2.xml, чтобы объединить, производит:

<DATA>
  <ITEM>
    <STYLE_COLOR>1524740007</STYLE_COLOR>
    <NEXT_ARRIVAL>2011-08-05</NEXT_ARRIVAL>
    <CLASS_NAME>XXX</CLASS_NAME>
    <OTHER>YYY</OTHER>
  </ITEM>
  <ITEM>
    <STYLE_COLOR>1524740008</STYLE_COLOR>
    <NEXT_ARRIVAL>2011-08-05</NEXT_ARRIVAL>
  </ITEM>
</DATA>

производит:

<DATA>
   <ITEM>
      <NEXT_ARRIVAL>2011-08-05</NEXT_ARRIVAL>
      <OTHER>YYY</OTHER>
      <PRODUCT_TYPE>simple</PRODUCT_TYPE>
      <STYLE_COLOR>1524740007</STYLE_COLOR>
      <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
      <CLASS_NAME>FOOTWEAR</CLASS_NAME>
      <STATUS>Disabled</STATUS>
   </ITEM>
   <ITEM>
      <NEXT_ARRIVAL>2011-08-05</NEXT_ARRIVAL>
      <PRODUCT_TYPE>simple</PRODUCT_TYPE>
      <STYLE_COLOR>1524740008</STYLE_COLOR>
      <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
      <CLASS_NAME>FOOTWEAR</CLASS_NAME>
      <STATUS>Disabled</STATUS>
   </ITEM>
</DATA>

Обратите внимание:

  • преобразование не не переопределяет существующие элементы для данного ПУНКТА, просто скопируйте отсутствующие
  • ПУНКТ в input1.xml копируется в вывод, только если есть совпадение в input2.xml
1 голос
/ 29 июля 2011

Это преобразование (c:/temp/file1.xml и c:/temp/file2.xml, как указано в вопросе):

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

 <xsl:param name="pDoc1" select="'file:///c:/temp/file1.xml'"/>
 <xsl:variable name="vDoc1" select="document($pDoc1)"/>

 <xsl:param name="pDoc2" select="'file:///c:/temp/file2.xml'"/>
 <xsl:variable name="vDoc2" select="document($pDoc2)"/>

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

 <xsl:template match="/">
  <xsl:apply-templates select="$vDoc1/node()"/>
 </xsl:template>

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

   <xsl:apply-templates select=
     "$vDoc2/*/ITEM
               [STYLE_COLOR = current()/STYLE_COLOR]
                /node()[not(self::STYLE_COLOR)]
     "/>
  </ITEM>
 </xsl:template>
</xsl:stylesheet>

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

<DATA>
   <ITEM xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <PRODUCT_TYPE>simple</PRODUCT_TYPE>
      <STYLE_COLOR>1524740007</STYLE_COLOR>
      <SHORT_DESCRIPTION>Black Shoe</SHORT_DESCRIPTION>
      <CLASS_NAME>FOOTWEAR</CLASS_NAME>
      <STATUS>Disabled</STATUS>
      <NEXT_ARRIVAL>2011-08-05</NEXT_ARRIVAL>
   </ITEM> ...
</DATA>

Объяснение

  1. Сначала мы обрабатываем (применяем шаблоны) узлы первого XML-документа .

  2. Правило / шаблон идентификации копирует каждый узел как .

  3. Существует один шаблон, который переопределяет правило идентификации . Этот шаблон соответствует любому элементу с именем ITEM. Он создает элемент с именем ITEM, затем обрабатывает все дочерние узлы, в результате чего их копирует шаблон идентификации. Наконец, все узлы, являющиеся потомками любого элемента ITEM из второго XML-документа, чей дочерний элемент STYLE_COLOR имеет то же строковое значение, что и строковое значение дочернего элемента STYLE_COLOR текущего (совпавшего) элемента, также копируются (посредством применяя к ним шаблоны и в результате выбора и выполнения шаблона идентификации), за исключением самого дочернего элемента STYLE_COLOR.

  4. Обратите внимание, что пути к файлам двух патчей передаются как параметры для преобразования , что делает его более гибким и способным работать с любыми двумя файлами XML - без каких-либо изменений. Мы используем функцию XSLT document() для загрузки и анализа этих двух документов, чтобы их можно было обработать с помощью преобразования xslt.

  5. Обратите внимание, что , * или никакие другие условные инструкции XSLT не используются *1057* в этом преобразовании. Это упрощает и упрощает поддержку и понимание кода.

  6. Наконец, обратите внимание на использование и переопределение правила / шаблона идентификации. Это самый фундаментальный и мощный шаблон проектирования XSLT .

0 голосов
/ 16 июня 2011

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

print "<FOOCONTAINER>\n";
readfile($xml1file);
readfile($xml2file);
print "</FOOCONTAINER>\n";

Все, что вы можете легко выполнить в дальнейшей обработке, так как это XML.

Редактировать: Это работает только для первого XML, предложенного OP.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...