使用document()在XSLT中交叉引用和扩充XML元素的问题

时间:2012-04-17 18:41:20

标签: xslt xpath

我是XML的新手,我遇到了一个问题,我生成的XML文档已经生成,某些元素的属性列表不完整。我正在尝试实现一个XSLT样式表,它将它与主文档交叉引用(它包含所有属性的默认值),以便使用默认值填充任何缺少的属性。

例如,采用以下不完整的XML文档:

<?xml version="1.0" encoding="utf-8"?>
<foo>
  <bar label="two" index="2"/>
</foo>

bar元素有一个缺少的属性'type',我想根据'label'的值从以下主文档填充默认值:

<?xml version="1.0" encoding="utf-8"?>
<foo>
  <bar label="one" index="1" type="type1"/>
  <bar label="two" index="2" type="type2"/>
  <bar label="three" index="3" type="type3"/>
</foo>

期望的结果是:

<?xml version="1.0" encoding="utf-8"?>
<foo>
  <bar label="two" index="2" type="type2"/>
</foo>

我的XSLT样式表尝试使用'document()'和XPath的组合执行此操作,如下所示:

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

  <xsl:variable name="master" select="document('master.xml')"/>

  <!-- Template matching 'root' of XML document -->
  <xsl:template match="/">
    <xsl:apply-templates select="foo"/>
  </xsl:template>

  <!-- Template for generating 'foo' element -->
  <xsl:template match="foo">
    <foo>
      <xsl:apply-templates select="bar"/>
    </foo>
  </xsl:template>

  <!-- Template for generating 'bar' element -->
  <xsl:template match="bar">
    <bar label="{@label}" index="{@index}" type="{$master/foo/bar[@label=@label][1]/@type}"/>
  </xsl:template>

</xsl:stylesheet>

然而,这不起作用,并为'bar'元素提供默认的'type'属性,'label'为'one'而不是'two':

<?xml version="1.0" encoding="utf-8"?>
<foo>
  <bar label="two" index="2" type="type1"/>
</foo>

一些调查显示XPath模式匹配所有'bar'元素而不仅仅是正确的元素,但我不确定原因。

我做错了什么,有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

问题在于此行

<bar label="{@label}" index="{@index}" 
     type="{$master/foo/bar[@label=@label][1]/@type}"/> 

您实质上是在检查label元素的bar属性是否等于它自己 - 这相当于:

<bar label="{@label}" index="{@index}" 
     type="{$master/foo/bar[true()][2]/@type}"/> 

,这相当于

<bar label="{@label}" index="{@index}" 
     type="{$master/foo/bar[1]/@type}"/> 

这就是您观察到的结果的产生方式。

解决方案:使用XSLT current() 功能:

<bar label="{@label}" index="{@index}" 
     type="{$master/foo/bar[@label=current()/@label][4]/@type}"/> 

完整转换(我已将网址更改为master.xml,以便能够在我的本地计算机上运行转换):

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

      <xsl:variable name="master" select=
      "document('file:///c:/temp/delete/master.xml')"/>

      <!-- Template matching 'root' of XML document -->
      <xsl:template match="/">
        <xsl:apply-templates select="foo"/>
      </xsl:template>

      <!-- Template for generating 'foo' element -->
      <xsl:template match="foo">
        <foo>
          <xsl:apply-templates select="bar"/>
        </foo>
      </xsl:template>

      <!-- Template for generating 'bar' element -->
      <xsl:template match="bar">
        <bar label="{@label}" index="{@index}"
        type="{$master/foo/bar[@label=current()/@label][5]/@type}"/>
      </xsl:template>
</xsl:stylesheet>

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

<foo>
    <bar label="two" index="2"/>
</foo>

产生了想要的正确结果:

<foo><bar label="two" index="2" type="type2"/></foo>

<强> II。使用keys

可能提高解决方案的效率
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kTypeByLabel" match="@type" use="../@label"/>

      <xsl:variable name="master" select=
      "document('file:///c:/temp/delete/master.xml')"/>

      <!-- Template matching 'root' of XML document -->
      <xsl:template match="/">
        <xsl:apply-templates select="foo"/>
      </xsl:template>

      <!-- Template for generating 'foo' element -->
      <xsl:template match="foo">
        <foo>
          <xsl:apply-templates select="bar"/>
        </foo>
      </xsl:template>

      <!-- Template for generating 'bar' element -->
      <xsl:template match="bar">
        <xsl:variable name="vCurrent" select="."/>
        <xsl:variable name="vDefault">
          <xsl:for-each select="$master">
            <xsl:value-of select=
                "key('kTypeByLabel', $vCurrent/@label)"/>
          </xsl:for-each>
        </xsl:variable>

        <bar label="{@label}" index="{@index}" type="{$vDefault}"/>
      </xsl:template>
</xsl:stylesheet>

当在(相同的)提供的XML文档(上面)上应用此转换时,会生成所需的正确结果

<foo>
   <bar label="two" index="2" type="type2"/>
</foo>