2010-09-03 64 views
2

我想知道如何合并2个XML流,然后再次进行最终转换。使用关键值上的XSLT匹配合并两个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节点值与Date节点的value属性之间的匹配。

还要注意合并需要合并两种方式。

+0

我已经编辑好你的输入样本了。如果有差异,则回滚。另外,在转换之前不需要合并它:'fn:document'允许多个输入源。如果你添加你的第二步转换,有人可以提供一个例子。 – 2010-09-03 15:52:12

+0

@ Neil-Gallagher:您需要在您的问题中指定如何处理相同名称和定位的元素。例如,如果两个文档中都有'/ */*/Instrument/Date/Saf',但这两个元素的值不同,那么应该选择这两个值中的哪一个? – 2010-09-03 21:35:47

+0

@ Neil-Gallagher:查看我的答案,找出不使用'count()'函数执行设置成员资格操作的解决方案。 – 2010-09-04 04:17:56

回答

2

这个样式表:

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

注意:使用的应用模板允许同时运行合并和第二阶段转型。此外,使用的fn:document用于多输入源,和XPath测试列入:count($node|$node-set)=count($node-set)

编辑:连键是相同的。它看起来像MSXSL4有一个错误,这就是为什么我尤斯$vSource2[last()]代替$vSource2[1]

0

这种转变(包括整个第二份文件 - 只是为了方便):

<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. 最后,不具有相应的从第1文件<Instrument>节点第二文档中的所有节点<Instrument>,复制到输出。

  4. 匹配Date来自两个文件的节点使用密钥来标识。