XML到CSV仅检索动态属性

时间:2015-08-06 10:48:15

标签: xml csv xslt xslt-1.0

基于my previous question

我们对XML结构进行了更改,因此输入现在看起来像这样:

<EXPORT>
  <DOCUMENTS>
    <DOCUMENT>
      <INDEX NAME="NAME" VALUE="folder"/>
      <INDEX NAME="LOCATION" VALUE="C:\here"/>
    </DOCUMENT>
    <DOCUMENT>
      <INDEX NAME="COLOR" VALUE="blue"/>
      <INDEX NAME="LOCATION" VALUE="C:\here"/>
      <INDEX NAME="DATE" VALUE="01-25-2015"/>
    </DOCUMENT>
  </DOCUMENTS>
</EXPORT>

对于此示例,我想获得以下CSV输出:

NAME,LOCATION,COLOR,DATE
folder, c:\here,,
,C:\here,blue,01-25-2015

我痛苦地尝试了很多东西,但我不知道generate-id是如何工作的。我最终完成了不完整的XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:variable name="delimiter" select="','"/>

  <xsl:key name="fields" match="/EXPORT/DOCUMENTS/*" use="./@name"/>

  <xsl:variable name="Fields"
       select="/EXPORT/DOCUMENTS/*[generate-id()=generate-id(key('fields', local-name())[1])]" />

  <xsl:template match="/">
    <xsl:for-each select="$Fields">
      <xsl:value-of select="local-name()" />
      <xsl:if test="position() &lt; last()">
        <xsl:value-of select="$delimiter" />
      </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="*/*" />
  </xsl:template>

  <xsl:template match="*">
    <xsl:variable name="this" select="." />
    <xsl:for-each select="$Fields">
      <xsl:value-of select="$this/*[local-name() = local-name(current())]" />
      <xsl:if test="position() &lt; last()">
        <xsl:value-of select="$delimiter" />
      </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

我应该改变什么?你有更好的方法吗?

1 个答案:

答案 0 :(得分:3)

你的第一个问题是关键

 <xsl:key name="fields" match="/EXPORT/DOCUMENTS/*" use="./@name"/>

这只是获取DOCUMENT元素,当你真正想要INDEX元素时。此外,它区分大小写,因此属性为@NAME而非@name

<xsl:key name="fields" match="/EXPORT/DOCUMENTS/DOCUMENT/*" use="@NAME"/>

但是,当你使用密钥时,你正在使用local-name()很多,但这会得到元素的名称(在这种情况下,总是INDEX),所以你需要全部替换local-name()的出现,而是获取NAME属性。

例如:

<xsl:variable name="Fields"
   select="/EXPORT/DOCUMENTS/DOCUMENT/*[generate-id()=generate-id(key('fields', @NAME)[1])]" />

另请注意,输出字段值时,您希望输出VALUE属性的值,而不是元素本身的实际文本值

<xsl:value-of select="$this/*[@NAME = $name]/@VALUE" />

试试这个XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:variable name="delimiter" select="','"/>

  <xsl:key name="fields" match="/EXPORT/DOCUMENTS/DOCUMENT/*" use="./@NAME"/>

  <xsl:variable name="Fields"
       select="/EXPORT/DOCUMENTS/DOCUMENT/*[generate-id()=generate-id(key('fields', @NAME)[1])]" />

  <xsl:template match="/">
    <xsl:for-each select="$Fields">
      <xsl:value-of select="@NAME" />
      <xsl:if test="position() &lt; last()">
        <xsl:value-of select="$delimiter" />
      </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="*/*/*" />
  </xsl:template>

  <xsl:template match="*">
    <xsl:variable name="this" select="." />
    <xsl:for-each select="$Fields">
      <xsl:variable name="name" select="@NAME" />
      <xsl:value-of select="$this/*[@NAME = $name]/@VALUE" />
      <xsl:if test="position() &lt; last()">
        <xsl:value-of select="$delimiter" />
      </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>