【问题标题】:Generate CSV output using XSL使用 XSL 生成 CSV 输出
【发布时间】:2013-09-26 22:56:34
【问题描述】:

我在这个结构中有一个 xml 文件(见下文),我需要从中生成 csv 输出。

<Root>
  <Metadata>
   <id>A001</id>
   <name>Test</name>
  </Metadata>
  <Employers>
    <Employer id="111">
      <Employee id="aaa"><Name>Rick</Name></Employee>
      <Employee id="bbb"><Name>Ram</Name></Employee>
    </Employer>
    <Employer id="222">
      <Employee id="ddd"><Name>Bob</Name></Employee>
      <Employee id="dcc"><Name>Tan</Name></Employee>
    </Employer>
  </Employers>
</Root>

使用 xsl 我需要生成如下的 csv 输出:

A001, Test, 111, aaa, Rick
A001, Test, 111, bbb, Ram
A001, Test, 222, ddd, Bob
A001, Test, 222, dcc, Tan

谁能告诉我如何生成这个? 仅供参考,我能够生成雇主数据元素但无法生成元数据 每个和每个 Employer 行的元素。

【问题讨论】:

  • 您是否希望关注Employers 和另一个Metadata 以及更多Employers
  • 我首先需要元数据元素(即每一行),然后是雇主详细信息。
  • 我明白这一点。我的问题是您的输入 XML 中是否有 两个或更多 Metadata 元素,每个元素后面都有单独的 Employers?您需要 XSLT 1.0 还是 XSLT 2.0?
  • 不是只有一个元数据元素。我使用的是 1.0 版。

标签: xml xslt


【解决方案1】:

这里是一个遵循 RFC4180 的解决方案的抄本。逗号后面的多余空格不应该在那里。

数据:

T:\ftemp>type emp2csv.xml 
<Root>
  <Metadata>
   <id>A001</id>
   <name>Test</name>
  </Metadata>
  <Employers>
    <Employer id="111">
      <Employee id="aaa"><Name>Rick</Name></Employee>
      <Employee id="bbb"><Name>Ram</Name></Employee>
    </Employer>
    <Employer id="222">
      <Employee id="ddd"><Name>Bob</Name></Employee>
      <Employee id="dcc"><Name>Tan</Name></Employee>
    </Employer>
  </Employers>
</Root>

执行:

T:\ftemp>call xslt emp2csv.xml emp2csv.xsl 
A001,Test,111,aaa,Rick
A001,Test,111,bbb,Ram
A001,Test,222,ddd,Bob
A001,Test,222,dcc,Tan

样式表:

T:\ftemp>type emp2csv.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="commonFields"
              select="/*/Metadata/id | /*/Metadata/name"/>

<xsl:template match="/">
  <xsl:apply-templates select="Root/Employers/Employer/Employee"/>
</xsl:template>

<!--these elements are CSV fields-->
<xsl:template match="Employee">
  <xsl:for-each select="$commonFields | ../@id | @id | Name">
    <xsl:call-template name="doThisField"/>
    <xsl:if test="position() != last()">,</xsl:if>
  </xsl:for-each>
  <xsl:text>&#xa;</xsl:text>
</xsl:template>

<!--put out a field escaping content-->
<xsl:template name="doThisField">
  <!--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>

编辑删除了多余的模板规则。

【讨论】:

  • 太棒了!非常感谢您帮助我。
  • 不错的解决方案 - 我没想过在我的解决方案中包含字符转义。
【解决方案2】:
<?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" indent="yes"/>
  <xsl:template match="/Root">
    <xsl:apply-templates select="Employers/Employer/Employee" />
  </xsl:template>
  <xsl:template match="/Root/Employers/Employer/Employee">
    <xsl:value-of select="../../../Metadata/id"/>
    <xsl:call-template name="delim" />
    <xsl:value-of select="../../../Metadata/name"/>
    <xsl:call-template name="delim" />
    <xsl:value-of select="../@id"/>
    <xsl:call-template name="delim" />
    <xsl:value-of select="@id"/>
    <xsl:call-template name="delim" />
    <xsl:value-of select="./Name"/>
    <xsl:call-template name="linebreak" />
  </xsl:template>
  <xsl:template name="delim">
    <xsl:text>, </xsl:text>
  </xsl:template>
  <xsl:template name="linebreak">
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

如果您想要 Windows 样式的换行符(例如,在大多数语言中相当于 \n\r\n),请使用 &lt;xsl:text&gt;&amp;#xD;&amp;#xA;&lt;/xsl:text&gt;(回车 + 换行)代替 &lt;xsl:text&gt;&amp;#xA;&lt;/xsl:text&gt;(换行)。

注意:分隔符和换行符位于它们自己的模板中,这样您就可以轻松地修改字符而无需在多个位置进行更新/不必深入研究用于将数据拉到一起的模板定义。

【讨论】:

  • 当你尝试它时会发生什么?您使用的是什么 XSL 引擎(仅供参考:我在 Visual Studio 2010 中编写/测试过,也在这里测试过:xslfiddle.net)。我已经修改以删除上面的几个 MS 特定位,以防万一。 . .
  • ps。早些时候,我修改了您问题中的 XML,因为它缺少员工元素的结束标记 - 如果在您的源 XML 中这也不正确,这可能是问题的原因;我在发布问题时以为这只是一个错字,所以之前没想过要突出显示它。
猜你喜欢
  • 2017-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-20
  • 1970-01-01
相关资源
最近更新 更多