将大量XML文件展平并转换为表格

时间:2010-05-18 13:34:05

标签: xml xslt

我有大量(2k +)的xml文件,我需要从中提取数据并将数据转储到某种类的表中(Excel或只是一个或多个XML文件就可以了)。 有趣的是,xml文件具有截然不同的节点数量(偶尔在子节点中具有相同的名称),并且层次结构的深度也是未知的。

无论如何,这是一个例子:

<?xml version="1.0" encoding="UTF-8"?>
   <SomeName>
     <identifier>
        <UID> 1234 </UID>
     </identifier>
     <MainNode1>
         <SubNode1>
            <Subnode1a>DATA1a0</Subnode1a>
         </SubNode1>
         <SubNode1>
            <Subnode1a>DATA1a1</Subnode1a>
         </SubNode1>
         <SubNode1>
            <Subnode1a>DATA1a2</Subnode1a>
         </SubNode1>
      </MainNode1>

      <MainNode2>
         <SubNode2>
            <Subnode2a>DATA2a0</Subnode2a>
         </SubNode2>
         <SubNode3>
            <Subnode3a>DATA3a0</Subnode3a>
         </SubNode3>
         <SubNode4>
            <Subnode4a>DATA4a0</Subnode4a>
         </SubNode4>
      </MainNode2>

      <MainNodeIDONTCARE>
           <SubnodeWhatever>
           </SubnodeWhatever>
      <MainNodeIDONTCARE>
    </SomeName>

这是我的桌子,我希望它能够展平。基本上它应该是这样的:

 <?xml version="1.0" encoding="UTF-8"?>
 <SomeName> 
 <UID>1234</UID>
 <MainNode1_SubNode1_SubNode1aA>DATA1a0</MainNode1_SubNode1_SubNode1aA>
 <MainNode1_SubNode1_SubNode1aB>DATA1a1</MainNode1_SubNode1_SubNode1aB>
 <MainNode1_SubNode1_SubNode1aC>DATA1a2</MainNode1_SubNode1_SubNode1aC>
 <MainNode2_SubNode2_SubNode2a>Data2a0</MainNode2_SubNode2_SubNode2a>
 <MainNode2_SubNode3_SubNode3a>Data3a0</MainNode2_SubNode3_SubNode3a>
 <MainNode2_SubNode4_SubNode4a>Data4a0</MainNode2_SubNode4_SubNode4a>
 </SomeName>

正如您所看到的,<MainNodeIDONTCARE>不在表中,因为我只想从XML文件中提取特定的数据集(我想要包含我需要的MainNodes,而忽略所有其他的)。在这种情况下,MainNode1到MainNode4中的任何内容。

完成这项工作的最佳方法是什么?我不关心性能,因为文件不是太大,转换可能需要的时间长。

是否有一些聪明的XSLT或其他任何可以帮助我的东西? 谢谢。

3 个答案:

答案 0 :(得分:0)

无论是否使用XSLT(并且是可行的),似乎诀窍是创建一个xpath语句,该语句构成所有这些节点的单个集合。正确的xpath语句不关心深度。

答案 1 :(得分:0)

你在这里:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

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

  <xsl:template match="UID" priority="1">
    <xsl:copy-of select="."/>
  </xsl:template>

  <xsl:template match="MainNodeIDONTCARE" priority="1"/> 

  <xsl:template match="SomeName//*[not(*)][text()]">
    <xsl:variable name="elementName">
      <xsl:call-template name="getElementName">
        <xsl:with-param name="element" select="."/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:element name="{$elementName}">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

  <xsl:template name="getElementName">
    <xsl:param name="element"/>

    <xsl:if test="$element/parent::*[not(self::SomeName)]">
      <xsl:call-template name="getElementName">
        <xsl:with-param name="element" select="$element/parent::*"/>
      </xsl:call-template>
      <xsl:text>_</xsl:text>
    </xsl:if>
    <xsl:value-of select="name($element)"/>
  </xsl:template>

</xsl:stylesheet>

答案 2 :(得分:0)

使用XSLT 2.0 [Saxon](我认为这不适用于xslt 1.0):

首先,非空文本节点包含数据,因此请选择它们:

  <xsl:template match="/SomeName" >        
      <SomeName>
          <xsl:copy-of select="identifier/UID" />
        <xsl:apply-templates select="(MainNode1|MainNode2)//text()[normalizespace()]" />
      </SomeName>
  </xsl:template>

然后你需要通过加入祖先名称来创建一个元素名称:

<xsl:template match="text()" >
  <xsl:element  name="{string-join( ./ancestor::*[name()!='SomeName']/name(),'_')}">
    <xsl:value-of select="." />
  </xsl:element>
</xsl:template>