Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xslt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
通用XML到CSV转换_Xml_Xslt_Csv_Export To Csv - Fatal编程技术网

通用XML到CSV转换

通用XML到CSV转换,xml,xslt,csv,export-to-csv,Xml,Xslt,Csv,Export To Csv,我正在尝试将动态XML转换为CSV。我搜索了各种选项来实现这一点,但没有找到合适的答案 XML的结构是动态的——它可以是产品数据、地理数据或任何类似的东西。因此,我不能使用预定义的XSL或castor转换 标记名应构成CSV的标题。 例如: <Ctry> <datarow> <CtryName>Ctry1</CtryName> <CtryID>12361</CtryID> <State&

我正在尝试将动态XML转换为CSV。我搜索了各种选项来实现这一点,但没有找到合适的答案

XML的结构是动态的——它可以是产品数据、地理数据或任何类似的东西。因此,我不能使用预定义的XSL或castor转换

标记名应构成CSV的标题。 例如:

<Ctry>
  <datarow>
     <CtryName>Ctry1</CtryName>
     <CtryID>12361</CtryID>
    <State>
      <datarow>
         <StateName>State1</StateName>
         <StateID>12361</StateID>
        <City>
           <datarow>
              <CityName>City1</CityName>
               <CityID>12361</CityID>
           </datarow>
        </City>
      </datarow>
      <datarow>
         <StateName>State2</StateName>
         <StateID>12361</StateID>
      </datarow>
      </State>
  </datarow>
</Ctry>

你能推荐一个合适的方法来解决这个问题吗?

下面是一个文本,演示了如何执行通用样式表来进行这种转换。样式表所做的唯一假设是元素
。给定的结构意味着根据请求的结果使用子元素:

数据:

样式表:

  T:\ftemp>type xml2csv.xsl 
  <?xml version="1.0" encoding="US-ASCII"?>
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  version="2.0">

  <xsl:output method="text"/>

  <xsl:variable name="fields" 
                select="distinct-values(//datarow/*[not(*)]/name(.))"/>

  <xsl:template match="/">
    <!--header row-->
    <xsl:value-of select="$fields" separator=","/>

    <!--body-->
    <xsl:apply-templates select="*"/>

    <!--final line terminator-->
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>

  <!--elements only process elements, not text-->
  <xsl:template match="*">
    <xsl:apply-templates select="*"/>
  </xsl:template>

  <!--these elements are CSV fields-->
  <xsl:template match="datarow/*[not(*)]">
    <!--replicate ancestors if necessary-->
    <xsl:if test="position()=1 and ../preceding-sibling::datarow">
      <xsl:for-each select="ancestor::datarow[position()>1]/*[not(*)]">
        <xsl:call-template name="doThisField"/>
      </xsl:for-each>
    </xsl:if>
    <xsl:call-template name="doThisField"/>
  </xsl:template>

  <!--put out a field ending the previous field and escaping content-->
  <xsl:template name="doThisField">
    <xsl:choose>
      <xsl:when test="name(.)=$fields[1]">
        <!--previous line terminator-->
        <xsl:text>&#xa;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <!--previous field terminator-->
        <xsl:text>,</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <!--field value escaped per RFC4180-->
    <xsl:choose>
      <xsl:when test="contains(.,'&#x22;') or 
                      contains(.,',') or
                      contains(.,'&#xa;')">
        <xsl:text>"</xsl:text>
        <xsl:value-of select="replace(.,'&#x22;','&#x22;&#x22;')"/>
        <xsl:text>"</xsl:text>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  </xsl:stylesheet>
T:\ftemp>type xml2csv.xsl

;

;
,
"
"
请注意,上述代码根据RFC4180转义各个字段

我的个人资料有一个指向我的网站的链接,在那里你可以找到一个免费的XML资源目录,其中包括一个XSLT样式表,用于将RFC4180 CSV文件转换为XML文件

根据原始海报的要求,这是答案的XSLT 1.0解决方案:

t:\ftemp>type xml2csv1.xsl 
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">

<xsl:output method="text"/>

<xsl:variable name="firstFieldName" 
              select="name((//datarow/*[not(*)])[1])"/>

<xsl:key name="names" match="datarow/*[not(*)]" use="name(.)"/>

<xsl:template match="/">
  <!--header row-->
  <xsl:for-each select="//datarow/*[not(*)]
                        [generate-id(.)=
                         generate-id(key('names',name(.))[1])]">
    <xsl:if test="position()>1">,</xsl:if>
    <xsl:value-of select="name(.)"/>
  </xsl:for-each>

  <!--body-->
  <xsl:apply-templates select="*"/>

  <!--final line terminator-->
  <xsl:text>&#xa;</xsl:text>
</xsl:template>

<!--elements only process elements, not text-->
<xsl:template match="*">
  <xsl:apply-templates select="*"/>
</xsl:template>

<!--these elements are CSV fields-->
<xsl:template match="datarow/*[not(*)]">
  <!--replicate ancestors if necessary-->
  <xsl:if test="position()=1 and ../preceding-sibling::datarow">
    <xsl:for-each select="ancestor::datarow[position()>1]/*[not(*)]">
      <xsl:call-template name="doThisField"/>
    </xsl:for-each>
  </xsl:if>
  <xsl:call-template name="doThisField"/>
</xsl:template>

<!--put out a field ending the previous field and escaping content-->
<xsl:template name="doThisField">
  <xsl:choose>
    <xsl:when test="name(.)=$firstFieldName">
      <!--previous line terminator-->
      <xsl:text>&#xa;</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <!--previous field terminator-->
      <xsl:text>,</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
  <!--field value escaped per RFC4180-->
  <xsl:choose>
    <xsl:when test="contains(.,'&#x22;') or 
                    contains(.,',') or
                    contains(.,'&#xa;')">
      <xsl:text>"</xsl:text>
      <xsl:call-template name="escapeQuote"/>
      <xsl:text>"</xsl:text>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!--escape a double quote in the current node value with two double quotes-->
<xsl:template name="escapeQuote">
  <xsl:param name="rest" select="."/>
  <xsl:choose>
    <xsl:when test="contains($rest,'&#x22;')">
      <xsl:value-of select="substring-before($rest,'&#x22;')"/>
      <xsl:text>""</xsl:text>
      <xsl:call-template name="escapeQuote">
        <xsl:with-param name="rest" select="substring-after($rest,'&#x22;')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$rest"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>type xml2csv1.xsl
,

;

;
,
"
"
""

下面是一份演示执行通用样式表以进行此类转换的转录本。样式表所做的唯一假设是元素
。给定的结构意味着根据请求的结果使用子元素:

数据:

样式表:

  T:\ftemp>type xml2csv.xsl 
  <?xml version="1.0" encoding="US-ASCII"?>
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  version="2.0">

  <xsl:output method="text"/>

  <xsl:variable name="fields" 
                select="distinct-values(//datarow/*[not(*)]/name(.))"/>

  <xsl:template match="/">
    <!--header row-->
    <xsl:value-of select="$fields" separator=","/>

    <!--body-->
    <xsl:apply-templates select="*"/>

    <!--final line terminator-->
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>

  <!--elements only process elements, not text-->
  <xsl:template match="*">
    <xsl:apply-templates select="*"/>
  </xsl:template>

  <!--these elements are CSV fields-->
  <xsl:template match="datarow/*[not(*)]">
    <!--replicate ancestors if necessary-->
    <xsl:if test="position()=1 and ../preceding-sibling::datarow">
      <xsl:for-each select="ancestor::datarow[position()>1]/*[not(*)]">
        <xsl:call-template name="doThisField"/>
      </xsl:for-each>
    </xsl:if>
    <xsl:call-template name="doThisField"/>
  </xsl:template>

  <!--put out a field ending the previous field and escaping content-->
  <xsl:template name="doThisField">
    <xsl:choose>
      <xsl:when test="name(.)=$fields[1]">
        <!--previous line terminator-->
        <xsl:text>&#xa;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <!--previous field terminator-->
        <xsl:text>,</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <!--field value escaped per RFC4180-->
    <xsl:choose>
      <xsl:when test="contains(.,'&#x22;') or 
                      contains(.,',') or
                      contains(.,'&#xa;')">
        <xsl:text>"</xsl:text>
        <xsl:value-of select="replace(.,'&#x22;','&#x22;&#x22;')"/>
        <xsl:text>"</xsl:text>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  </xsl:stylesheet>
T:\ftemp>type xml2csv.xsl

;

;
,
"
"
请注意,上述代码根据RFC4180转义各个字段

我的个人资料有一个指向我的网站的链接,在那里你可以找到一个免费的XML资源目录,其中包括一个XSLT样式表,用于将RFC4180 CSV文件转换为XML文件

根据原始海报的要求,这是答案的XSLT 1.0解决方案:

t:\ftemp>type xml2csv1.xsl 
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">

<xsl:output method="text"/>

<xsl:variable name="firstFieldName" 
              select="name((//datarow/*[not(*)])[1])"/>

<xsl:key name="names" match="datarow/*[not(*)]" use="name(.)"/>

<xsl:template match="/">
  <!--header row-->
  <xsl:for-each select="//datarow/*[not(*)]
                        [generate-id(.)=
                         generate-id(key('names',name(.))[1])]">
    <xsl:if test="position()>1">,</xsl:if>
    <xsl:value-of select="name(.)"/>
  </xsl:for-each>

  <!--body-->
  <xsl:apply-templates select="*"/>

  <!--final line terminator-->
  <xsl:text>&#xa;</xsl:text>
</xsl:template>

<!--elements only process elements, not text-->
<xsl:template match="*">
  <xsl:apply-templates select="*"/>
</xsl:template>

<!--these elements are CSV fields-->
<xsl:template match="datarow/*[not(*)]">
  <!--replicate ancestors if necessary-->
  <xsl:if test="position()=1 and ../preceding-sibling::datarow">
    <xsl:for-each select="ancestor::datarow[position()>1]/*[not(*)]">
      <xsl:call-template name="doThisField"/>
    </xsl:for-each>
  </xsl:if>
  <xsl:call-template name="doThisField"/>
</xsl:template>

<!--put out a field ending the previous field and escaping content-->
<xsl:template name="doThisField">
  <xsl:choose>
    <xsl:when test="name(.)=$firstFieldName">
      <!--previous line terminator-->
      <xsl:text>&#xa;</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <!--previous field terminator-->
      <xsl:text>,</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
  <!--field value escaped per RFC4180-->
  <xsl:choose>
    <xsl:when test="contains(.,'&#x22;') or 
                    contains(.,',') or
                    contains(.,'&#xa;')">
      <xsl:text>"</xsl:text>
      <xsl:call-template name="escapeQuote"/>
      <xsl:text>"</xsl:text>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!--escape a double quote in the current node value with two double quotes-->
<xsl:template name="escapeQuote">
  <xsl:param name="rest" select="."/>
  <xsl:choose>
    <xsl:when test="contains($rest,'&#x22;')">
      <xsl:value-of select="substring-before($rest,'&#x22;')"/>
      <xsl:text>""</xsl:text>
      <xsl:call-template name="escapeQuote">
        <xsl:with-param name="rest" select="substring-after($rest,'&#x22;')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$rest"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>type xml2csv1.xsl
,

;

;
,
"
"
""

听起来你在要求一个程序读懂你对XML结构的想法。-1你写过任何可能对你有帮助的代码吗?如果你只是想让别人为你写代码,那么在一个自由职业者的网站上你可能会有更好的运气。我已经编写了一个基于SAX的解析器,但它不能完全解决这个问题。因此,我一直在寻找任何一个java库,它可以很容易地做到这一点。我有XML的XSD。基于此,我正试图编写一个通用XSLT,将其转换为CSV。任何编写通用XSLT的指针都会有所帮助。我刚刚为这个问题编写了一个通用XSLT解决方案。我不明白为什么它必须被标记为XSLT的非主题。我还在我的网站上免费提供了一些从CSV到XML的开发人员资源。这里发布的原始需求似乎非常通用:我唯一的假设是
元素定义了要在CSV中捕获的字段集。我对元素名没有其他假设,所以这是一个非常通用的解决方案。为什么每个人都认为这不仅仅是另一项XSLT样式表编写任务?听起来你是在要求一个程序读懂你对XML结构的想法。-1你写过任何可能对你有帮助的代码吗?如果你只是想让别人为你写代码,那么在一个自由职业者的网站上你可能会有更好的运气。我已经编写了一个基于SAX的解析器,但它不能完全解决这个问题。因此,我一直在寻找任何一个java库,它可以很容易地做到这一点。我有XML的XSD。基于此,我正试图编写一个通用XSLT,将其转换为CSV。任何编写通用XSLT的指针都会有所帮助。我刚刚为这个问题编写了一个通用XSLT解决方案。我不明白为什么它必须被标记为XSLT的非主题。我还在我的网站上免费提供了一些从CSV到XML的开发人员资源。这里发布的原始需求似乎非常通用:我唯一的假设是
元素定义了要在CSV中捕获的字段集。我对元素名没有其他假设,所以这是一个非常通用的解决方案。为什么每个人都认为这不仅仅是另一个XSLT样式表编写任务?嗨,肯,谢谢你的回复。。。我试图运行我的java程序,但出现了此异常。。“发生异常:javax.xml.transform.TransformerConfiguration异常:javax.xml.transform.TransformerConfiguration异常:javax.xml.transform.TransformerException:javax.xml.transform.TransformerException:找不到函数:不同的值”。。。我尝试了不同版本的xalan.jar。您能告诉我如何解决这个问题吗?您所谓的“问题”只是您没有在原始问题中指出您仅限于XSLT 1.0。我使用XSLT2.0为您提供了答案。由于我在用户界面中看不到发布替代答案的方法,因此我编辑了此答案,以包含解决方案的XSLT1.0版本!非常感谢!嗨,肯,比
t:\ftemp>type xml2csv1.xsl 
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">

<xsl:output method="text"/>

<xsl:variable name="firstFieldName" 
              select="name((//datarow/*[not(*)])[1])"/>

<xsl:key name="names" match="datarow/*[not(*)]" use="name(.)"/>

<xsl:template match="/">
  <!--header row-->
  <xsl:for-each select="//datarow/*[not(*)]
                        [generate-id(.)=
                         generate-id(key('names',name(.))[1])]">
    <xsl:if test="position()>1">,</xsl:if>
    <xsl:value-of select="name(.)"/>
  </xsl:for-each>

  <!--body-->
  <xsl:apply-templates select="*"/>

  <!--final line terminator-->
  <xsl:text>&#xa;</xsl:text>
</xsl:template>

<!--elements only process elements, not text-->
<xsl:template match="*">
  <xsl:apply-templates select="*"/>
</xsl:template>

<!--these elements are CSV fields-->
<xsl:template match="datarow/*[not(*)]">
  <!--replicate ancestors if necessary-->
  <xsl:if test="position()=1 and ../preceding-sibling::datarow">
    <xsl:for-each select="ancestor::datarow[position()>1]/*[not(*)]">
      <xsl:call-template name="doThisField"/>
    </xsl:for-each>
  </xsl:if>
  <xsl:call-template name="doThisField"/>
</xsl:template>

<!--put out a field ending the previous field and escaping content-->
<xsl:template name="doThisField">
  <xsl:choose>
    <xsl:when test="name(.)=$firstFieldName">
      <!--previous line terminator-->
      <xsl:text>&#xa;</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <!--previous field terminator-->
      <xsl:text>,</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
  <!--field value escaped per RFC4180-->
  <xsl:choose>
    <xsl:when test="contains(.,'&#x22;') or 
                    contains(.,',') or
                    contains(.,'&#xa;')">
      <xsl:text>"</xsl:text>
      <xsl:call-template name="escapeQuote"/>
      <xsl:text>"</xsl:text>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!--escape a double quote in the current node value with two double quotes-->
<xsl:template name="escapeQuote">
  <xsl:param name="rest" select="."/>
  <xsl:choose>
    <xsl:when test="contains($rest,'&#x22;')">
      <xsl:value-of select="substring-before($rest,'&#x22;')"/>
      <xsl:text>""</xsl:text>
      <xsl:call-template name="escapeQuote">
        <xsl:with-param name="rest" select="substring-after($rest,'&#x22;')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$rest"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>