Объединение двух потоков XML с использованием XSLT-сопоставления по значениям ключа - PullRequest
2 голосов
/ 03 сентября 2010

Я хотел бы знать, как объединить 2 потока XML, прежде чем, наконец, снова его преобразовать.

Два потока

Ввод 1

<Response>
    <Instrument>
        <Date value="2010-09-02">
            <Quantity>10</Quantity>
        </Date>
        <DXLID>1</DXLID>
    </Instrument>
    <Instrument TICKER="APPL" />
    <SF></SF>
    <Instrument>
        <Date value="2010-09-02">
            <Quantity>20</Quantity>
        </Date>
        <DXLID>2</DXLID>
    </Instrument>
    <Instrument TICKER="APPL" />
    <SF></SF>
</Response>

Вход 2

<Response>
    <IM>
        <Instrument>
            <Date value="2010-09-02">
                <SAF>1</SAF>
                <SAR>2</SAR>
            </Date>
            <DXLID>1</DXLID>
        </Instrument>
        <Instrument>
            <Date value="2010-09-02">
                <SAF>1</SAF>
                <SAR>2</SAR>
            </Date>
            <DXLID>3</DXLID>
        </Instrument>
    </IM>
</Response>

Желаемый выход

<Response>
    <All>
        <Instrument>
            <Date value="2010-09-02">
                <SAF>1</SAF>
                <SAR>2</SAR>
                <Quantity>10</Quantity>
            </Date>
            <DXLID>1</DXLID>
        </Instrument>
        <Instrument>
            <Date value="2010-09-02">
                <Quantity>20</Quantity>
            </Date>
            <DXLID>2</DXLID>
        </Instrument>
        <Instrument>
            <Date value="2010-09-02">
                <SAF>1</SAF>
                <SAR>2</SAR>
            </Date>
            <DXLID>3</DXLID>
        </Instrument>
    </All>
</Response>

Слияние должно основываться на совпадении значения узла DXLID и атрибута value узла Date.

Обратите внимание, что объединение должно быть объединением в обоих направлениях.

Ответы [ 2 ]

2 голосов
/ 03 сентября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kInstrumentByDateAndDXLID" match="Instrument"
             use="concat(Date/@value,'++',DXLID)"/>
    <xsl:variable name="vSource1"
               select="document('Doc1.xml')/Response/Instrument[Date][DXLID]"/>
    <xsl:variable name="vSource2"
               select="document('Doc2.xml')/Response/IM/Instrument"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/">
        <Response>
            <All>
                <xsl:apply-templates select="$vSource1|$vSource2">
                    <xsl:sort select="DXLID"/>
                </xsl:apply-templates>
            </All>
        </Response>
    </xsl:template>
    <xsl:template match="Date/*[last()]">
        <xsl:call-template name="identity"/>
        <xsl:if test="count(../..|$vSource1)=count($vSource1)">
            <xsl:variable name="vKey"
                          select="concat(../@value,'++',../../DXLID)"/>
            <xsl:for-each select="$vSource2[last()]">
                <xsl:apply-templates
                 select="key('kInstrumentByDateAndDXLID',$vKey)/Date/*"/>
            </xsl:for-each>
        </xsl:if>
    </xsl:template>
    <xsl:template match="Instrument">
        <xsl:if test="count(.|$vSource1)=count($vSource1) or
                      not($vSource1[key('kInstrumentByDateAndDXLID',
                                        concat(current()/Date/@value,'++',
                                               current()/DXLID))])">
            <xsl:call-template name="identity"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Вывод:

<Response>
    <All>
        <Instrument>
            <Date value="2010-09-02">
                <Quantity>10</Quantity>
                <SAF>1</SAF>
                <SAR>2</SAR>
            </Date>
            <DXLID>1</DXLID>
        </Instrument>
        <Instrument>
            <Date value="2010-09-02">
                <Quantity>20</Quantity>
            </Date>
            <DXLID>2</DXLID>
        </Instrument>
        <Instrument>
            <Date value="2010-09-02">
                <SAF>1</SAF>
                <SAR>2</SAR>
            </Date>
            <DXLID>3</DXLID>
        </Instrument>
    </All>
</Response>

Примечание : использование apply-templates позволяет запускать слияние и преобразование второго шага водин раз.Кроме того, использование fn:document для нескольких источников ввода и проверка XPath для включения: count($node|$node-set)=count($node-set)

Редактировать : то же самое с клавишами.Похоже, что MSXSL4 имеет ошибку, поэтому я использую $vSource2[last()] вместо $vSource2[1]

0 голосов
/ 04 сентября 2010

Это преобразование (включает весь второй документ - только для удобства):

<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:key name="kDateByValAndId" match="Date"
  use="concat(@value, '+', ../DXLID)"/>

 <xsl:variable name="vDoc1" select="/"/>

 <xsl:variable name="vrtfDoc2">
    <Response>
        <IM>
            <Instrument>
                <Date value="2010-09-02">
                    <SAF>1</SAF>
                    <SAR>2</SAR>
                </Date>
                <DXLID>1</DXLID>
            </Instrument>
            <Instrument>
                <Date value="2010-09-02">
                    <SAF>1</SAF>
                    <SAR>2</SAR>
                </Date>
                <DXLID>3</DXLID>
            </Instrument>
        </IM>
    </Response>
 </xsl:variable>

 <xsl:variable name="vDoc2" select=
  "document('')/*/xsl:variable[@name='vrtfDoc2']"/>

 <xsl:template match="/">
  <Response>
   <All>
    <xsl:apply-templates select="/*/node()"/>
    <xsl:apply-templates mode="doc2" select=
     "$vDoc2/*/*/Instrument[Date and DXLID]" />
   </All>
  </Response>
 </xsl:template>

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

 <xsl:template match="Date">
  <xsl:variable name="vkeyVal" select=
  "concat(@value, '+', ../DXLID)"/>

  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
   <xsl:for-each select="$vDoc2">
    <xsl:apply-templates select="key('kDateByValAndId', $vkeyVal)/node()"/>
   </xsl:for-each>
  </xsl:copy>
 </xsl:template>
 <xsl:template match="SF|Instrument[@TICKER='APPL']"/>

 <xsl:template match="Instrument" mode="doc2">
  <xsl:variable name="vkeyVal" select=
  "concat(Date/@value, '+', DXLID)"/>

  <xsl:variable name="vcur" select="."/>

  <xsl:for-each select="$vDoc1">
   <xsl:if test="not(key('kDateByValAndId', $vkeyVal))">
    <xsl:copy-of select="$vcur"/>
   </xsl:if>
  </xsl:for-each>

 </xsl:template>
</xsl:stylesheet>

при применении к первому 1-му документу :

<Response>
    <Instrument>
        <Date value="2010-09-02">
            <Quantity>10</Quantity>
        </Date>
        <DXLID>1</DXLID>
    </Instrument>
    <Instrument TICKER="APPL" />
    <SF></SF>
    <Instrument>
        <Date value="2010-09-02">
            <Quantity>20</Quantity>
        </Date>
        <DXLID>2</DXLID>
    </Instrument>
    <Instrument TICKER="APPL" />
    <SF></SF>
</Response>

дает желаемый, правильный результат :

<Response>
   <All>
      <Instrument>
         <Date value="2010-09-02">
            <Quantity>10</Quantity>
            <SAF xmlns:xsl="http://www.w3.org/1999/XSL/Transform">1</SAF>
            <SAR xmlns:xsl="http://www.w3.org/1999/XSL/Transform">2</SAR>
         </Date>
         <DXLID>1</DXLID>
      </Instrument>
      <Instrument>
         <Date value="2010-09-02">
            <Quantity>20</Quantity>
         </Date>
         <DXLID>2</DXLID>
      </Instrument>
      <Instrument xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
         <Date value="2010-09-02">
            <SAF>1</SAF>
            <SAR>2</SAR>
         </Date>
         <DXLID>3</DXLID>
      </Instrument>
   </All>
</Response>

Примечание:

  1. Узлы пространства имен не появятся, если второй документ находится в своем собственном файле - он включен в текущее преобразование просто для удобства.

  2. Все узлы первого документа обрабатываются первыми (если у некоторых из них есть совпадающие узлы во втором документе, выполняется объединение).

  3. Наконец, все <Instrument> узлы из второго документа, не имеющие соответствующих <Instrument> узлов из 1-го документа, копируются в вывод.

  4. Соответствующие Date узлы из двух документов идентифицируются с помощью ключей .

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