XML в CSV извлекает только динамические атрибуты

На основе мой предыдущий вопрос.

Мы изменили структуру 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>

Что мне следует изменить? У вас есть лучший подход?


person Kilazur    schedule 06.08.2015    source источник


Ответы (1)


Ваша первая проблема с ключом

 <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>
person Tim C    schedule 06.08.2015
comment
Большое спасибо! Бонус за пояснения, чаще всего я не находил ответов xslt, содержащих только простой ответ... Отлично! - person Kilazur; 06.08.2015