将XML文件转换为CSV

时间:2010-03-25 15:28:24

标签: xml csv xslt

使用正则表达式转换混乱的XML后,我现在需要再次更改它。 这个源文件

<product>
    <sku>SP00001</sku>
    <PID_OWNER_SellerID>StoreName</PID_OWNER_SellerID>
    <EANCode>8711983489813</EANCode>
    <DeliveryDays>2</DeliveryDays>
</product>

必须成为CSV文件,但是像这样:

sku        field                 value
SP00001    PID_OWNER_SellerID    StoreName
SP00001    EANCode               8711983489813
SP00001    DeliveryDays          2

我认为这是在正则表达式的范围之外,必须用XSL完成?

4 个答案:

答案 0 :(得分:6)

尝试使用正则表达式解析XML通常是一个坏主意,因为有一种无限的方式来格式化结构相同的XML文档,但是会破坏你的正则表达式。

对于非大规模的文件,一定要使用XSL,记住指定'text'作为输出方法。不要忘记,如果必须,您可以以编程方式调用XSL进程 - 大多数语言都允许您这样做。

对于大文件,请考虑编写一个使用流API的小程序(例如SAX或其中一个推送解析器API)。

答案 1 :(得分:4)

以下是一些XSLT ...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
                exclude-result-prefixes="msxsl"
    >
  <xsl:output method="text" indent="yes"/>

  <xsl:template match="/">
    <xsl:call-template name="headerRow" />
    <xsl:apply-templates select="//product" />
  </xsl:template>

  <xsl:template name="headerRow">
    <xsl:call-template name="rightpad">
      <xsl:with-param name="fieldvalue" select="'sku'"/>
      <xsl:with-param name="fieldsize" select="number(11)"/>
    </xsl:call-template>

    <xsl:call-template name="rightpad">
      <xsl:with-param name="fieldvalue" select="'field'"/>
      <xsl:with-param name="fieldsize" select="number(22)"/>
    </xsl:call-template>

    <xsl:text>value&#xd;&#xa;</xsl:text>
  </xsl:template>

  <xsl:template match="product">
    <xsl:for-each select="node()[local-name(.) != 'sku']">

      <xsl:call-template name="rightpad">
        <xsl:with-param name="fieldvalue" select="../sku"/>
        <xsl:with-param name="fieldsize" select="number(11)"/>
      </xsl:call-template>

      <xsl:call-template name="rightpad">
        <xsl:with-param name="fieldvalue" select="local-name(.)"/>
        <xsl:with-param name="fieldsize" select="number(22)"/>
      </xsl:call-template>

      <xsl:value-of select="."/>
      <xsl:text>&#xd;&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="rightpad">
    <xsl:param name="fieldvalue" select="string('')"/>
    <xsl:param name="fieldsize" select="0"/>

    <xsl:variable name="padded" 
                  select="concat($fieldvalue, '                   ')" />
    <xsl:variable name="result" 
                  select="substring($padded,1,$fieldsize)" />

    <xsl:value-of select="$result"/>
  </xsl:template>

</xsl:stylesheet>

答案 2 :(得分:1)

XPathDocumemt x = new XPathDocument("yourdoc.xml");    
XPathNavigator n = x.CreateNavigator();    
XPathNodeIterator i = n.Select("root/product");

List<string> fields = new List<string>() { "PID_OWNER_SellerID", "EANCode", "DeliveryDays" }

using (TextWriter w = File.CreateText("c:\\yourfile.csv"))
{
    w.WriteLine("sku, field, value");

    while (i.MoveNext())
    {
        foreach (string field in fields)
        {
            w.WriteLine(string.Format("{0}, {1}, {2}", i.Current.SelectSingleNode("sku").value, field, i.Current.selectSingleNode(field).Value));
        }
    }
}

答案 3 :(得分:1)

此样式表将以指定的格式生成输出:

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

  <!--Spacing between column1 and column2 is 11 characters-->
  <xsl:variable name="col1-spaces" select="'           '" />
  <!--Spacing between column2 and column3 is 22 characters-->
  <xsl:variable name="col2-spaces" select="concat($col1-spaces, $col1-spaces)" />

  <xsl:template match="/">
    <!--Generate the heading row first, then apply templates-->
    <xsl:text>sku</xsl:text>
    <!--Add enough spaces after to align the next column-->
    <xsl:value-of select="substring($col1-spaces, 3)"/>
    <xsl:text>field</xsl:text>
    <!--Add enough spaces after to align the next column-->
    <xsl:value-of select="substring($col2-spaces, 5)"/>
    <xsl:text>value&#10;</xsl:text>
    <xsl:apply-templates />
  </xsl:template>

  <!--Do nothing with sku elements-->
  <xsl:template match="sku" />

  <!--For all elements that are children of product, except for sku, do this-->
  <xsl:template match="product/*[not(self::sku)]">
        <xsl:value-of select="preceding-sibling::sku"/>
        <!--Calculate how many spaces are needed using the length of the value of sku -->
        <xsl:value-of select="substring($col1-separator, string-length(preceding-sibling::sku))"/>
        <xsl:value-of select="local-name()" />
        <!--Calculate how many spaces are needed using the length of the name of the current element-->
        <xsl:value-of select="substring($col2-separator, string-length(local-name()))"/>
        <xsl:value-of select="." />
        <xsl:text>&#10;</xsl:text>
  </xsl:template>

</xsl:stylesheet>