【问题标题】:XSL 1.0 Format table splitting valuesXSL 1.0 格式化表格拆分值
【发布时间】:2013-10-07 15:24:08
【问题描述】:

我有一个示例 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>

【问题讨论】:

  • 嘿,如果你多次改变初始条件,那就不是问题了,这是“请做我的工作”!...
  • 对不起 Orabig,实际上我没有更改条件,因为要解析的初始结构是相同的,但在文件中会重复。你的建议和其他人一样工作正常,但目前他们都以不同的方式管理字段和行。我试图通过表格绕过这个循环,但到目前为止还没有得到解决方案

标签: xml xslt split


【解决方案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 非常不适合此类事情...

【讨论】:

    【解决方案2】:

    如果您可以确定行值中唯一的逗号是分隔字段的逗号(即没有像 "\," 这样的“转义”机制),并且每行中总会有正确数量的字段,那么递归方法将起作用。我在这里假设有一个名为 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>
    

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

    【讨论】:

    • @AndreaPappy 根据您的最新编辑,我已调整我的答案以处理同一文档中的多个表格。
    【解决方案3】:

    这是一个可以处理多个字段的解决方案,假设它具有由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>
    

    【讨论】:

    • 我一直在使用您的解决方案,它几乎是完美的,但我不得不检查一下输入文件,所以我在字段名称方面遇到了一个小问题。您能否详细说明一下您的解决方案,以便使用为 esch 可能的输入表分隔的字段名称?谢谢
    猜你喜欢
    • 2017-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-26
    相关资源
    最近更新 更多