1

我需要用 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 做到这一点?这甚至可能吗?

4

2 回答 2

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>
于 2012-06-05T13:41:51.940 回答
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()

于 2012-06-05T13:56:38.237 回答