0

我有一个示例 xml 输入,如下所示:

<table name="Table1">
<fields>
   <field name="Field1" />
   <field name="Field2" />
   <field name="Field3" />
   <field name="Field4" />
</fields>
<data>
   <row value="2,Description1,A,AA" />
   <row value="3,Description2,B,BB" />
   <row value="7,Description3,C,CC" />
</data>
</table>
<table name="Table2">
<fields>
   <field name="Field7" />
   <field name="Field8" />
   <field name="Field9" />
</fields>
<data>
   <row value="Q,Description7,A" />
   <row value="W,Description8,B" />
   <row value="X,Description9,C" />
</data>
</table>

请注意,我可以有许多具有不同字段数的表,但行值始终包含所需的确切字段数。

预期的结果是这样的输出:

<ListOfTable1>
<item>
   <Field1>2</Field1>
   <Field2>Description1</Field2>
   <Field3>A</Field3>
   <Field4>AA</Field4>
</item>
<item>
   <Field1>3</Field1>
   <Field2>Description2</Field2>
   <Field3>B</Field3>
   <Field4>BB</Field4>
</item>
<item>
   <Field1>7</Field1>
   <Field2>Description3</Field2>
   <Field3>C</Field3>
   <Field4>CC</Field4>
</item>
</ListOfTable1>

<ListOfTable2>
<item>
   <Field7>Q</Field7>
   <Field8>Description7</Field8>
   <Field9>A</Field9>
</item>
<item>
   <Field7>W</Field7>
   <Field8>Description8</Field8>
   <Field9>B</Field9>
</item>
<item>
   <Field7>X</Field7>
   <Field8>Description9</Field8>
   <Field9>C</Field9>
</item>
</ListOfTable2>

不幸的是,我只能使用严格的 XSLT 1.0 没有外部函数或参考

我一直在使用建议的第三个解决方案的略微修改版本

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

  <xsl:template match="//table">
    <xsl:value-of disable-output-escaping="yes" select="concat('&lt;ListOf',@name,'&gt;')" />
    <xsl:apply-templates />
    <xsl:value-of disable-output-escaping="yes" select="concat('&lt;/ListOf',@name,'&gt;')" />
  </xsl:template>

  <xsl:template match="table/data/row">
    <item>
      <xsl:call-template name="fldsplit">
        <xsl:with-param name="f" select="@value" />
        <xsl:with-param name="set" select="//fields/field" />
      </xsl:call-template>
    </item>
  </xsl:template>

  <xsl:template name="fldsplit">
    <xsl:param name="f" />
    <xsl:param name="set"/>
    <xsl:variable name="bfc" select="substring-before($f,',')"/>
    <xsl:variable name="afc" select="substring-after($f,',')"/>
    <xsl:element name="{$set/@name}">
      <xsl:choose>
        <xsl:when test="$bfc">
          <xsl:value-of select="$bfc"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$f"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:element>
    <xsl:if test="$afc">
      <xsl:call-template name="fldsplit">
        <xsl:with-param name="f" select="$afc"/>
        <xsl:with-param name="set" select="$set/following-sibling::*" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

但是我对第二个表(或我添加到输入的任何其他表)有一个问题,即字段名称总是从 Field1 开始重复,而我希望为每个解析的表使用特定字段

这是当前输出(错误)

<?xml version="1.0" encoding="UTF-8"?>

<ListOfTable1>
   <item><Field1>2</Field1><Field2>Description1</Field2><Field3>A</Field3><Field4>AA</Field4></item>
   <item><Field1>3</Field1><Field2>Descritpion2</Field2><Field3>B</Field3><Field4>BB</Field4></item>
   <item><Field1>7</Field1><Field2>Description3</Field2><Field3>C</Field3><Field4>CC</Field4></item>
</ListOfTable1>
<ListOfTable2>
   <item><Field1>Q</Field1><Field2>Description7</Field2><Field3>A</Field3></item>
   <item><Field1>W</Field1><Field2>Description8</Field2><Field3>B</Field3></item>
   <item><Field1>X</Field1><Field2>Description9</Field2><Field3>C</Field3></item>
</ListOfTable2>
4

3 回答 3

1

好吧,这应该可以按预期工作:

<xsl:template match="/">
    <result>
        <xsl:apply-templates select="//row"/>
    </result>
</xsl:template>

<xsl:template match="row">
    <xsl:variable name="field1" select="substring-before(@value,',')"/>
    <xsl:variable name="field2" select="substring-after(@value,',')"/>
    <item>
       <Field1><xsl:value-of select="$field1"/></Field1>
       <Field2><xsl:value-of select="$field2"/></Field2>
    </item>
</xsl:template>

如您所见,我没有使用您的部分(顺便说一句,Field2 的 type="int" 似乎不适合示例),因为我不知道如何使用。它应该是多个字段吗?(如果可能存在未定义数量的字段,则需要递归解决方案......)


编辑 :

好的,我已经设法编写了一个使用任意数量字段的解决方案:

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

<xsl:key name="allfields" match="field" use="count(preceding-sibling::field) + 1"/>

<xsl:template match="/">
    <result>
        <xsl:apply-templates select="//row"/>
    </result>
</xsl:template>

<xsl:template match="row">
    <item>
    <xsl:call-template name="row">
        <xsl:with-param name="value" select="@value"/>
        <xsl:with-param name="pos" select="1"/>
    </xsl:call-template>
    </item>
</xsl:template>

<xsl:template name="row">
    <xsl:param name="value"/>
    <xsl:param name="pos"/>
    <xsl:variable name="field1">
        <xsl:choose>
            <xsl:when test="contains($value,',')">
                <xsl:value-of select="substring-before($value,',')"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$value"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:variable name="fieldNext" select="substring-after($value,',')"/>
    <xsl:variable name="fieldName" select="key('allfields',$pos)/@name"/>
        <xsl:element name="{$fieldName}">
            <xsl:value-of select="$field1"/>
        </xsl:element>
        <xsl:if test="not($fieldNext='')">
            <xsl:call-template name="row">
                <xsl:with-param name="value" select="$fieldNext"/>
                <xsl:with-param name="pos" select="$pos + 1"/>
            </xsl:call-template>
        </xsl:if>
</xsl:template>
</xsl:stylesheet>

这表明 XSLT1.0 非常不适合此类事情......

于 2013-10-07T15:41:15.043 回答
0

如果您可以确定行值中唯一的逗号是分隔字段的逗号(即没有像 ' 这样的“转义”机制"\,"),并且每行中总会有正确数量的字段,那么递归方法将起作用. 我在这里假设有一个名为root封闭您的table元素的单个根元素,以使输入格式正确。

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

  <xsl:template match="/">
    <data>
      <xsl:apply-templates select="root/table" />
    </data>
  </xsl:template>

  <xsl:template match="table">
    <xsl:element name="ListOf{@name}">
      <xsl:apply-templates select="data/row"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="row">
    <!-- add a trailing comma to the value so we don't lose the last field -->
    <xsl:variable name="val" select="concat(@value, ',')" />
    <item>
      <!-- use just the fields that belong to this table -->
      <xsl:apply-templates select="../../fields/field[1]">
        <xsl:with-param name="thisVal" select="substring-before($val, ',')" />
        <xsl:with-param name="rest" select="substring-after($val, ',')" />
      </xsl:apply-templates>
    </item>
  </xsl:template>

  <xsl:template match="field">
    <xsl:param name="thisVal"/>
    <xsl:param name="rest"/>

    <xsl:element name="{@name}">
      <xsl:value-of select="$thisVal" />
    </xsl:element>

    <xsl:apply-templates select="following-sibling::field[1]">
      <xsl:with-param name="thisVal" select="substring-before($rest, ',')" />
      <xsl:with-param name="rest" select="substring-after($rest, ',')" />
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

这里的技巧是我们通过依次<Field1>将模板应用于元素来生成 etc. 元素。field因此不需要任何显式条件(ifchoose),因为当following-sibling::field[1]不再选择任何内容时,递归将自动停止在最后一个字段。

于 2013-10-07T16:17:58.033 回答
0

这是一个可以处理多个字段的解决方案,假设它具有由 a 分隔的相应数量的字段,

  <xsl:template match="/">
    <result>
    <xsl:apply-templates />
    </result>
  </xsl:template>

  <xsl:template match="table">
     <xsl:element name="ListOf{@Name}">
         <xsl:apply-templates />
     </xsl:element>
  </xsl:template>


  <xsl:template match="data/row">
    <item>
      <xsl:call-template name="fldsplit">
        <xsl:with-param name="f" select="@value" />
        <xsl:with-param name="set" select="//fields/field" />
      </xsl:call-template>
    </item>
  </xsl:template>

  <xsl:template name="fldsplit">
    <xsl:param name="f" />
    <xsl:param name="set"/>
    <xsl:variable name="bfc" select="substring-before($f,',')"/>
    <xsl:variable name="afc" select="substring-after($f,',')"/>
    <xsl:element name="{$set/@name}">
      <xsl:choose>
        <xsl:when test="$bfc">
          <xsl:value-of select="$bfc"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$f"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:element>
    <xsl:if test="$afc">
      <xsl:call-template name="fldsplit">
        <xsl:with-param name="f" select="$afc"/>
        <xsl:with-param name="set" select="$set/following-sibling::*" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
于 2013-10-07T16:04:08.547 回答