获取列表中每个元素的子字符串,并在XSLT中使它们不同

时间:2012-06-05 12:53:20

标签: xml xslt xpath

我需要使用XSLT转换XML文件,这项任务有点棘手。

我的名称为attr_1000_a的属性,而数字和后缀是动态的,因此attr_2000_b也是有效的。

此外,还有<row>个组合相关数据的元素。我需要对它们进行转换,以便将同等数量的属性(即attr_1000_aattr_1000_b)放入同一个元素中。

让我举个例子。输入XML后:

<root>
  <row id="1">
    <foo attr_1000_a="true">1</foo>
    <foo attr_1000_b="true">2</foo>
    <foo attr_1000_c="true">3</foo>
  </row>
  <row id="2">
    <foo attr_1000_a="true" attr_1000_b="true" attr_1000_c="true">10</foo>
    <foo attr_2000_a="true" attr_2000_b="true" attr_2000_c="true">20</foo>
  </row>
  <row id="3">
    <foo attr_1000_a="true" attr_2000_a="true" attr_3000_a="true">100</foo>
    <foo attr_1000_b="true" attr_2000_b="true" attr_3000_b="true">200</foo>
    <foo attr_1000_c="true" attr_2000_c="true" attr_3000_c="true">300</foo>
  </row>
</root>

您可以看到属性可以通过多种方式组合,这使得转换变得困难。每个属性在每个<row>中都是唯一的,但可以位于任何<foo>元素中。此外,每个<foo>可以具有任意数量的属性。

期望的结果:

<result>
  <row id="1">
    <field attr="1000">
      <a>1</a>
      <b>2</b>
      <c>3</c>
    </field>
  </row>
  <row id="2">
    <field attr="1000">
      <a>10</a>
      <b>10</b>
      <c>10</c>
    </field>
    <field attr="2000">
      <a>20</a>
      <b>20</b>
      <c>20</c>
    </field>
  </row>
  <row id="3">
    <field attr="1000">
      <a>100</a>
      <b>200</b>
      <c>300</c>
    </field>
    <field attr="2000">
      <a>100</a>
      <b>200</b>
      <c>300</c>
    </field>
    <field attr="3000">
      <a>100</a>
      <b>200</b>
      <c>300</c>
    </field>
  </row>
</result>

我想我必须以某种方式获得连续所有数字的列表(例如1000,2000和3000),然后遍历所有具有这种属性的元素。

我如何使用XSLT执行此操作?这甚至可能吗?

2 个答案:

答案 0 :(得分:3)

以下是样式表示例:

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

<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>

<xsl:key name="k1" 
  match="row/foo/@*" 
  use="concat(generate-id(../..), '|', substring-before(substring-after(local-name(), '_'), '_'))"/>

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

<xsl:template match="row">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="foo/@*[generate-id() = generate-id(key('k1', concat(generate-id(../..), '|', substring-before(substring-after(local-name(), '_'), '_')))[1])]" mode="field"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="foo/@*" mode="field">
  <field attr="{substring-before(substring-after(local-name(), '_'), '_')}">
    <xsl:apply-templates select="key('k1', concat(generate-id(../..), '|', substring-before(substring-after(local-name(), '_'), '_')))"/>
  </field>
</xsl:template>

<xsl:template match="foo/@*">
  <xsl:element name="{substring-after(substring-after(local-name(), '_'), '_')}">
    <xsl:value-of select=".."/>
  </xsl:element>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:3)

快速而肮脏,这个xslt

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

    <xsl:output indent="yes"/>

    <xsl:template match="root">
        <result>
         <xsl:apply-templates select="@*|node()"/>
        </result>
    </xsl:template>

    <xsl:template match="foo/@*">
        <xsl:element name="{substring-after(local-name(),'000_')}">
            <xsl:value-of select=".."/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="row">
        <row id="{@id}">
            <xsl:for-each-group select="foo/@*" group-by="substring(local-name(),1,9)">
                <field attr="{substring-after(current-grouping-key(),'attr_')}">
                   <xsl:apply-templates select="current-group()"/>
                </field>
            </xsl:for-each-group>
        </row>
    </xsl:template>

    <xsl:template match="foo">
        <xsl:apply-templates  select="@*"/>
    </xsl:template>

     <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>   
</xsl:stylesheet>

已应用于此输入

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <row id="1">
        <foo attr_1000_a="true">1</foo>
        <foo attr_1000_b="true">2</foo>
        <foo attr_1000_c="true">3</foo>
    </row>
    <row id="2">
        <foo attr_1000_a="true" attr_1000_b="true" attr_1000_c="true">10</foo>
        <foo attr_2000_a="true" attr_2000_b="true" attr_2000_c="true">20</foo>
    </row>
    <row id="3">
        <foo attr_1000_a="true" attr_2000_a="true" attr_3000_a="true">100</foo>
        <foo attr_1000_b="true" attr_2000_b="true" attr_3000_b="true">200</foo>
        <foo attr_1000_c="true" attr_2000_c="true" attr_3000_c="true">300</foo>
    </row>
</root>

产生此结果

<?xml version="1.0" encoding="UTF-8"?>
<result>
    <row id="1">
      <field attr="1000">
         <a>1</a>
         <b>2</b>
         <c>3</c>
      </field>
   </row>
    <row id="2">
      <field attr="1000">
         <a>10</a>
         <b>10</b>
         <c>10</c>
      </field>
      <field attr="2000">
         <a>20</a>
         <b>20</b>
         <c>20</c>
      </field>
   </row>
    <row id="3">
      <field attr="1000">
         <a>100</a>
         <b>200</b>
         <c>300</c>
      </field>
      <field attr="2000">
         <a>100</a>
         <b>200</b>
         <c>300</c>
      </field>
      <field attr="3000">
         <a>100</a>
         <b>200</b>
         <c>300</c>
      </field>
   </row>
</result>

魔法在

    <xsl:element name="{substring-after(local-name(),'000_')}">
        <xsl:value-of select=".."/>
    </xsl:element>

这将创建具有动态名称的a / b / c元素,并向上移动一个节点以从父节点获取值(我们当前位于该属性中)。

        <xsl:for-each-group select="foo/@*" group-by="substring(local-name(),1,9)">
            <field attr="{substring-after(current-grouping-key(),'attr_')}">
               <xsl:apply-templates select="current-group()"/>
            </field>
        </xsl:for-each-group>

使用他们的名称(foo/@*)的一部分重新组合所有属性(substring(local-name(),1,9))。第一个随后以current-group()形式提供,后者以current-grouping-key()形式提供,如您所见。