使用xsl:key检索兄弟姐妹

时间:2012-03-13 19:33:17

标签: xml xslt

我有一个表模式,其中包含一些元数据,我需要将其下拉到行级别。

所以这......

<tables>
    <table name="T01">
        <columns>
            <column name="F01">
                <heading>Field 1</heading>
            </column >
            <column name="F02">
                <heading>Field 2</heading>
            </column>
        </columns>
        <rows>
            <row>
                <field name="F01">AAAAA</field>
                <field name="F02">BBBBB</field>
            </row>
            <row>
                <field name="F01">DDDDD</field>
                <field name="F02">EEEEE</field>
            </row>
        </rows>
    </table>
    <table name="T02">
        <!-- ... -->
    </table>
</tables>

应该成为这个......

<tables>
    <table name="T01">
        <rows>
            <row>
                <field name="F01">
                    <heading>Field 1</heading>
                    <value>AAAAA</value>
                </field>
                <field name="F02">                    
                    <heading>Field 2</heading>  
                    <value>BBBBB</value>
                </field>
            </row>
            <row>
                <field name="F01">
                    <heading>Field 1</heading>
                    <value>DDDDD</value>
                </field>
                <field name="F02">                    
                    <heading>Field 2</heading>  
                    <value>EEEEE</value>
                </field>
            </row>
        </rows>
    </table>
    <table name="T02">
        <!-- ... -->
    </table>
</tables>

我确信有很多简单的方法可以使用XSLT来实现这一点,但由于我的工具,我真的需要通过key()检索列标题。所以像这样......

<?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" indent="yes"/>

  <!-- This won't work but if it did ... -->
  <xsl:key name="field-heading"
          match="../../columns/column/heading"
          use="../@name" />

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


  <xsl:template match="field">
    <field name="{@name}">
      <heading>
        <xsl:value-of select="key('field-heading', @name)"/>
      </heading>
      <value>
        <xsl:value-of select="."/>
      </value>
    </field>
  </xsl:template>

  <xsl:template match="columns"/>

</xsl:stylesheet>

但xsl:key match属性不允许父轴,我不确定是否还有其他方法使其适合。

3 个答案:

答案 0 :(得分:0)

主要问题是密钥定义的上下文是文档而不是您使用密钥的位置,因此定义应该是

<xsl:key name="field-heading"
    match="/tables/table/columns/column/heading"
    use="../@name" />

你的XML中有一些拼写错误 - 这就是我认为它应该是

<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet type="text/xsl" href="C:/Users/morank/Documents/temp/code.xsl"?>
<tables>
    <table name="T01">
        <columns>
            <column name="F01">
                <heading>Field 1</heading>
            </column>
            <column name="F02">
                <heading>Field 2</heading>
            </column>
        </columns>
        <rows>
            <row>
                <field name="F01">AAAAA</field>
                <field name="F02">BBBBB</field>
            </row>
            <row>
                <field name="F01">DDDDD</field>
                <field name="F02">EEEEE</field>
            </row>
        </rows>
    </table>
    <table name="T02">
        <!-- ... -->
    </table>
</tables>

应用以下XSLT

<?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" indent="yes"/>

    <!-- This won't work but if it did ... -->
    <xsl:key name="field-heading"
        match="/tables/table/columns/column/heading"
        use="../@name" />

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

    <xsl:template match="field">
        <field name='{@name}'>
            <heading>
                <xsl:value-of select="key('field-heading', @name)"/>
            </heading>
            <value>
                <xsl:value-of select="."/>
            </value>
        </field>
    </xsl:template>

    <xsl:template match="columns"/>

</xsl:stylesheet>

提供所需的输出:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="C:/Users/morank/Documents/temp/code.xsl"?><tables>
    <table name="T01">        
        <rows>
            <row>
                <field name="F01">
                    <heading>Field 1</heading>
                    <value>AAAAA</value>
                </field>
                <field name="F02">
                    <heading>Field 2</heading>
                    <value>BBBBB</value>
                </field>
            </row>
            <row>
                <field name="F01">
                    <heading>Field 1</heading>
                    <value>DDDDD</value>
                </field>
                <field name="F02">
                    <heading>Field 2</heading>
                    <value>EEEEE</value>
                </field>
            </row>
        </rows>
    </table>
    <table name="T02">
        <!-- ... -->
    </table>
</tables>

请注意,我还将模板更改为标准身份转换,并确保已复制字段名称

答案 1 :(得分:0)

尝试使用:

<xsl:key name="field-heading"
    match="heading"
    use="parent::column/@name" />

在这种情况下,../无法正常工作,因为use路径不是从root开始的。


修改

尝试使用table/@namecolumn/@name创建密钥。这应该是独一无二的。

XML输入(我希望这代表您评论中的问题。)

<tables>
  <table name="T01">
    <columns>
      <column name="F01">
        <heading>Field 1</heading>
      </column>
      <column name="F02">
        <heading>Field 2</heading>
      </column>
    </columns>
    <rows>
      <row>
        <field name="F01">AAAAA</field>
        <field name="F02">BBBBB</field>
      </row>
      <row>
        <field name="F01">DDDDD</field>
        <field name="F02">EEEEE</field>
      </row>
    </rows>
  </table>
  <table name="T02">
    <columns>
      <column name="F01">
        <heading>Field A</heading>
      </column>
      <column name="F02">
        <heading>Field B</heading>
      </column>
    </columns>
    <rows>
      <row>
        <field name="F01">11111</field>
        <field name="F02">22222</field>
      </row>
      <row>
        <field name="F01">44444</field>
        <field name="F02">55555</field>
      </row>
    </rows>
  </table>
</tables>

修改后的XSLT 1.0

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

  <!-- This won't work but if it did ... -->
  <xsl:key name="field-heading"
    match="heading"
    use="concat(ancestor::table/@name,'+',parent::column/@name)" />

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

  <xsl:template match="field">
    <field name="{@name}">
      <heading>
        <xsl:value-of select="key('field-heading', concat(ancestor::table/@name,'+',@name))"/>
      </heading>
      <value>
        <xsl:value-of select="."/>
      </value>
    </field>
  </xsl:template>

  <xsl:template match="columns"/>

</xsl:stylesheet>

XML输出

<tables>
  <table name="T01">

      <rows>
         <row>
            <field name="F01">
               <heading>Field 1</heading>
               <value>AAAAA</value>
            </field>
            <field name="F02">
               <heading>Field 2</heading>
               <value>BBBBB</value>
            </field>
         </row>
         <row>
            <field name="F01">
               <heading>Field 1</heading>
               <value>DDDDD</value>
            </field>
            <field name="F02">
               <heading>Field 2</heading>
               <value>EEEEE</value>
            </field>
         </row>
      </rows>
  </table>
  <table name="T02">

      <rows>
         <row>
            <field name="F01">
               <heading>Field A</heading>
               <value>11111</value>
            </field>
            <field name="F02">
               <heading>Field B</heading>
               <value>22222</value>
            </field>
         </row>
         <row>
            <field name="F01">
               <heading>Field A</heading>
               <value>44444</value>
            </field>
            <field name="F02">
               <heading>Field B</heading>
               <value>55555</value>
            </field>
         </row>
      </rows>
  </table>
</tables>

答案 2 :(得分:0)

稍微简单的解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kColByNames" match="column" use=
  "concat(../../@name, '+', @name)"/>

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

 <xsl:template match="field">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <heading>
     <xsl:value-of select=
      "key('kColByNames', concat(ancestor::table[1]/@name, '+', @name))"/>
   </heading>
   <value><xsl:value-of select="."/></value>
  </xsl:copy>
 </xsl:template>
 <xsl:template match="columns"/>
</xsl:stylesheet>

在提供的XML文档上应用此转换时

<tables>
    <table name="T01">
        <columns>
            <column name="F01">
                <heading>Field 1</heading>
            </column>
            <column name="F02">
                <heading>Field 2</heading>
            </column>
        </columns>
        <rows>
            <row>
                <field name="F01">AAAAA</field>
                <field name="F02">BBBBB</field>
            </row>
            <row>
                <field name="F01">DDDDD</field>
                <field name="F02">EEEEE</field>
            </row>
        </rows>
    </table>
    <table name="T02">
        <!-- ... -->
    </table>
</tables>

产生了想要的正确结果

<tables>
   <table name="T01">
      <rows>
         <row>
            <field name="F01">
               <heading>Field 1</heading>
               <value>AAAAA</value>
            </field>
            <field name="F02">
               <heading>Field 2</heading>
               <value>BBBBB</value>
            </field>
         </row>
         <row>
            <field name="F01">
               <heading>Field 1</heading>
               <value>DDDDD</value>
            </field>
            <field name="F02">
               <heading>Field 2</heading>
               <value>EEEEE</value>
            </field>
         </row>
      </rows>
   </table>
   <table name="T02"><!-- ... --></table>
</tables>

解释:覆盖身份规则并使用复合密钥 - columnname属性的标识,其祖父母name的{​​{1}}属性。