如何基于感应节点兄弟姐妹重构此xml文档?

时间:2011-07-20 11:15:59

标签: ruby xml xslt nokogiri

我有一个从库存系统输出的XML文档,其结构不是很好:

<root>
    <StockSalesRec Type="SH">                               
        <Reference>A Supplier</Reference>     
        <StockNum></StockNum>                      
        <Description></Description>
        ...
    </StockSalesRec>                                        
    <StockSalesRec Type="  ">                               
        <Reference>12345</Reference>     
        <StockNum>00001</StockNum>                      
        <Description>Item description</Description>
        ...
    </StockSalesRec>                                        
    <StockSalesRec Type="  ">                               
        <Reference>67890</Reference>     
        <StockNum>00002</StockNum>                      
        <Description>Another description</Description>
        ...
    </StockSalesRec>
    ...
</root>

每个<StockSalesRec Type=SH>都是供应商,下一个<StockSalesRec Type=SH>之前的所有内容都是该供应商的产品。在使用本文档之前,我想对其进行重组,使其看起来像:

<root>
    <supplier name="A Supplier">
        <product>
            <Reference>67890</Reference>     
            <StockNum>00002</StockNum>     
            <Description>Another description</Description>
        </product>
        ....
    </supplier>
    ....
</root>

我将如何以这种方式转换文档?我曾尝试使用XSL作为解决方案,但很快就卡住了。我最近正在学习Ruby,所以使用它们的解决方案会很棒。

由于

1 个答案:

答案 0 :(得分:1)

我认为这可以通过纯XSLT解决方案来完成。我通过首先为每个产品定义一个密钥(一个 StockSalesRec Type 的'')来实现它,其中查找为参考最新的前一个供应商记录( StockSalesRec ,其中类型为'SH')

<xsl:key 
  name="Stock" 
  match="StockSalesRec[@Type='  ']" 
  use="(preceding-sibling::StockSalesRec[@Type='SH'])[last()]/Reference" />

然后,您可以匹配每个供应商节点,如此

<xsl:apply-templates select="StockSalesRec[@Type='SH']" />

然后,对于每个此类供应商节点,您可以使用先前定义的密钥读取所有产品记录

<xsl:apply-templates select="key('Stock', Reference)" />

完全放下这个......

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

   <xsl:key 
      name="Stock" 
      match="StockSalesRec[@Type='  ']" 
      use="(preceding-sibling::StockSalesRec[@Type='SH'])[last()]/Reference"/>

   <xsl:template match="/root">
      <xsl:copy>
         <xsl:apply-templates select="StockSalesRec[@Type='SH']"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="StockSalesRec[@Type='SH']">
      <supplier name="{Reference}">
         <xsl:apply-templates select="key('Stock', Reference)"/>
      </supplier>
   </xsl:template>

   <xsl:template match="StockSalesRec[@Type='  ']">
      <product>
         <xsl:apply-templates/>
      </product>
   </xsl:template>

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

</xsl:stylesheet>

将此应用于示例XML时,输出如下:

<root>
   <supplier name="A Supplier">
      <product>
         <Reference>12345</Reference>
         <StockNum>00001</StockNum>
         <Description>Item description</Description>
      </product>
      <product>
         <Reference>67890</Reference>
         <StockNum>00002</StockNum>
         <Description>Another description</Description>
      </product>
   </supplier>
</root>

请注意在创建产品节点时使用identity变换,允许将额外的子元素添加到输入XML,而无需更改XSLT。