XSLT:将CSV从一个源XML节点拆分为多个目标XML节点

时间:2009-05-20 12:51:45

标签: xml xslt

感谢在SO this question帮助我的SO用户的帮助 我正在进行XML转换。

我使用转换逗号分隔值(DATE和QTY)撞墙的问题:

<?xml version="1.0" encoding="utf-8"?>
<results>
<recordset rowCount="68" fieldNames="ITEM,ECL,LEAD_TIME,QTY,DATE" type="**coldfusion.sql.QueryTable**">
<field name="ITEM">
<string>ITEM_A</string>
<string>ITEM_B</string>
<string>ITEM_C</string>
</field>
<field name="REV">
<string>A</string>
<string>B</string>
<string>C</string>
</field>
<field name="LEAD_TIME">
<string>10</string>
<string>15</string>
<string>25</string>
</field>

<field name="QTY">
<string>10,13,3</string>
<string>1,5,2</string>
<string>6,10,25</string>
</field>

</recordset>
<var name="DATE_LABELS">
<string>05-18,05-25,06-01</string>
</var>
</results>

分为:

<records>
<item_line>
 <item>ITEM_A</item>
 <rev>A</rev>
 <lead_time>10</lead_time>
 <values>
    <qty date="05-18>10</qty>
    <qty date="05-25>13</qty>
    <qty date="06-01>3</qty>
 </values>
</item_line>
<item_line>
 <item>ITEM_B</item>
 <rev>B</rev>
 <lead_time>15</lead_time>
 <values>
    <qty date="05-18>1</qty>
    <qty date="05-25>5</qty>
    <qty date="06-01>2</qty>
 </values>
</item_line>
<item_line>
 <item>ITEM_C</item>
 <rev>C</rev>
 <lead_time>25</lead_time>
 <values>
    <qty date="05-18>6</qty>
    <qty date="05-25>10</qty>
    <qty date="06-01>25</qty>
 </values>
</item_line>
</records>

是否可以用XSLT表达它?

我开始讨厌“设计”源xml架构的人......

更新:感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

我已将我提供的解决方案更新到您的其他问题:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes"/>

  <xsl:template match="/">
    <records>
      <!-- Just process the first field's children, to get the list of line items -->
      <xsl:apply-templates select="/results/recordset/field[1]/*"/>
    </records>
  </xsl:template>

          <!-- Convert each field child to a line item -->
          <xsl:template match="field/*">
            <item_line>
              <!-- Then query all the fields for the value at this position -->
              <xsl:apply-templates select="/results/recordset/field">
                <xsl:with-param name="pos" select="position()"/>
              </xsl:apply-templates>
            </item_line>
          </xsl:template>

                  <xsl:template match="field">
                    <xsl:param name="pos"/>
                    <!-- Convert the field name to lower case -->
                    <xsl:element name="{translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                                                        'abcdefghijklmnopqrstuvwxyz')}">
                      <xsl:value-of select="*[$pos]"/>
                    </xsl:element>
                  </xsl:template>

                  <xsl:template match="field[@name = 'QTY']">
                    <xsl:param name="pos"/>
                    <values>
                      <xsl:apply-templates mode="qty" select="*[$pos]"/>
                    </values>
                  </xsl:template>

  <xsl:template mode="qty" match="field/*">
    <!-- ASSUMPTION: date-list and value-list will have same number of commas -->
    <xsl:param name="date-list" select="/results/var[@name = 'DATE_LABELS']/string"/>
    <xsl:param name="value-list" select="."/>
    <!-- Extract the date -->
    <xsl:variable name="date">
      <xsl:call-template name="next-item">
        <xsl:with-param name="csv" select="$date-list"/>
      </xsl:call-template>
    </xsl:variable>
    <!-- Extract the value -->
    <xsl:variable name="value">
      <xsl:call-template name="next-item">
        <xsl:with-param name="csv" select="$value-list"/>
      </xsl:call-template>
    </xsl:variable>
    <!-- Output the element -->
    <qty date="{$date}">
      <xsl:value-of select="$value"/>
    </qty>
    <!-- Keep processing if there are more left -->
    <xsl:if test="contains($date-list,',')">
      <xsl:apply-templates mode="qty" select=".">
        <xsl:with-param name="date-list"  select="substring-after($date-list,  ',')"/>
        <xsl:with-param name="value-list" select="substring-after($value-list, ',')"/>
      </xsl:apply-templates>
    </xsl:if>
  </xsl:template>

          <xsl:template name="next-item">
            <xsl:param name="csv"/>
            <xsl:choose>
              <xsl:when test="contains($csv, ',')">
                <xsl:value-of select="substring-before($csv, ',')"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="$csv"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:template>

</xsl:stylesheet>

如果您有任何问题,请与我们联系。

答案 1 :(得分:1)

也许你下次可以在一个问题上问这块蛋糕,所以我们不必做两次,但我们走了!

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <xsl:template match="//recordset">
    <records>
      <xsl:apply-templates select="field[@name = 'ITEM']/string"/>
    </records>
  </xsl:template>

  <xsl:template match="//var" />

  <xsl:template match="string">
    <xsl:variable name="loc" select="position()"/>
    <item_line>
      <item>
        <xsl:value-of select="."/>
      </item>
      <rev>
        <xsl:value-of select="//recordset/field[@name = 'REV']/string[position() = $loc]"/>
      </rev>
      <lead_time>
        <xsl:value-of select="//recordset/field[@name = 'LEAD_TIME']/string[position() = $loc]"/>
      </lead_time>
      <values>
        <xsl:call-template name="split-csv">
          <xsl:with-param name="list">
            <xsl:value-of select="//recordset/field[@name = 'QTY']/string[position() = $loc]" />
          </xsl:with-param>
          <xsl:with-param name="labels">
            <xsl:value-of select="//var[@name = 'DATE_LABELS']/string" />
          </xsl:with-param>
        </xsl:call-template>
      </values>
    </item_line>
  </xsl:template>

  <xsl:template name="split-csv">
    <xsl:param name="list" />
    <xsl:param name="labels" />
    <xsl:variable name="first" select="substring-before($list, ',')" />
    <xsl:variable name="remaining" select="substring-after($list, ',')" />
    <xsl:variable name="label" select="substring-before($labels, ',')" />
    <qty>
      <xsl:choose>
        <xsl:when test="not($first) and $list">
          <xsl:attribute name="date">
            <xsl:value-of select="$labels"/>
          </xsl:attribute>
          <xsl:value-of select="$list" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:attribute name="date">
            <xsl:value-of select="$label"/>
          </xsl:attribute>
          <xsl:value-of select="$first" />
        </xsl:otherwise>
      </xsl:choose>
    </qty>
    <xsl:if test="$remaining">
      <xsl:call-template name="split-csv">
        <xsl:with-param name="list" select="$remaining" />
        <xsl:with-param name="labels" select="substring-after($labels, ',')" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>