0

我有一个具有这种结构的 xml 文件:

<a>
    <b attribute="54" name="Bob" ... >
        <c name="Foo" stuff="89" attr="First line&#xA;Second line" ... />
        <c name="Bar" stuff="23" attr="Blahs" ... />
        ...
    </b>
    ...
</a>

我想在 csv 文件中获取这些数据,如下所示:

b_attribute, b_name, ... , c1_name, c1_stuff, c1_attr ... , c2_name, c2_stuff, c2_attr ... 
"54", "Bob", ..., "Foo", "89", "First line&#xA;Second line", ..., "Bar", "23", "Blahs", ...

一些标签的值可能包含 html 逗号和引号。

4

1 回答 1

2

这个怎么样:

<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"
                xmlns:templates="csv-templates" xmlns:values="csv-values"
>
  <templates:header />
  <templates:value />
  <xsl:variable name="header" select="document('')//templates:header" />
  <xsl:variable name="value" select="document('')//templates:value" />

  <values:substitutions>
    <sub from="&quot;" to="&quot;quot;" />
    <sub from="," to="&quot;#x2C;" />
    <sub from="&#xA;" to="&amp;#xA;" />
  </values:substitutions>
  <xsl:variable name="substitutions" select="document('')//values:substitutions" />

  <xsl:output method="text" />

  <xsl:template match="text()" />

  <xsl:template match="/">
    <xsl:apply-templates select="*/*" mode="headers">
      <xsl:sort data-type="number" select="count(*)" order="descending"/>
    </xsl:apply-templates>
    <xsl:variable name="headerCount">
      <xsl:apply-templates select="*/*" mode="countAttributes">
        <xsl:sort data-type="number" select="count(*)" order="descending"/>
      </xsl:apply-templates>
    </xsl:variable>

    <xsl:apply-templates select="*/*">
      <xsl:with-param name="headerCount" select="$headerCount" />
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="/*/*">
    <xsl:param name="headerCount" />
    <xsl:text>&#xA;</xsl:text>
    <xsl:call-template name="CommaList">
      <xsl:with-param name="items" select=".//@*"/>
      <xsl:with-param name="type" select="$value" />
      <xsl:with-param name="minCount" select="$headerCount" />
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="/*/*" mode="headers">
    <xsl:if test="position() = 1">
      <xsl:call-template name="CommaList">
        <xsl:with-param name="items" select=".//@*"/>
        <xsl:with-param name="type" select="$header" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template match="/*/*" mode="countAttributes">
    <xsl:if test="position() = 1">
      <xsl:value-of select="count(.//@*)"/>
    </xsl:if>
  </xsl:template>

  <xsl:template match="templates:header" priority="2">
    <xsl:param name="item" />
    <xsl:variable name="index">
      <xsl:if test="count($item/ancestor::*) > 2">
        <xsl:value-of select="count($item/../preceding-sibling::*) + 1"/>
      </xsl:if>
    </xsl:variable>
    <xsl:value-of select="concat(local-name($item/..), $index, '_', local-name($item))"/>
  </xsl:template>

  <xsl:template match="templates:value" priority="2">
    <xsl:param name="item" />
    <xsl:if test="normalize-space($item)">
      <xsl:text>"</xsl:text>
      <xsl:call-template name="escape">
        <xsl:with-param name="value" select="$item" />
      </xsl:call-template>
      <xsl:text>"</xsl:text>
    </xsl:if>
  </xsl:template>

  <xsl:template name="CommaList">
    <xsl:param name="items" />
    <xsl:param name="type" />
    <xsl:param name="minCount" select="0" />

    <xsl:apply-templates select="$type">
      <xsl:with-param name="item" select="$items[1]" />
    </xsl:apply-templates>

    <xsl:variable name="remainder" select="$items[position() > 1]" />
    <xsl:if test="$remainder or $minCount > 1">
      <xsl:text>, </xsl:text>
      <xsl:call-template name="CommaList">
        <xsl:with-param name="items" select="$remainder" />
        <xsl:with-param name="type" select="$type" />
        <xsl:with-param name="minCount" select="$minCount - 1" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template name="escape">
    <xsl:param name="value"/>

    <xsl:variable name="foundToEscape" select="$substitutions/sub[contains($value, @from)]" />

    <xsl:choose>
      <xsl:when test="$foundToEscape">
        <xsl:call-template name="escape">
          <xsl:with-param name="value" select="substring-before($value, $foundToEscape/@from)" />
        </xsl:call-template>
        <xsl:value-of select="$foundToEscape/@to"/>
        <xsl:call-template name="escape">
          <xsl:with-param name="value" select="substring-after($value, $foundToEscape/@from)" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$value"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

可以通过向元素添加其他<sub>元素来添加其他替换规则<values:substitutions>

于 2013-01-14T14:53:25.253 回答