XSLT 2.0 - 将节点集作为参数传递似乎不起作用

时间:2013-07-12 21:27:13

标签: xslt

我有一个文档,我需要进行转换,以便按原样复制大多数元素,但有一些例外:对于某些指定的节点,需要追加子元素,并且其中一些子元素需要引用回特定的源文档中的元素。单独的“model / crosswalk”xml文件包含要添加的元素。人行横道中需要引用回源文档的元素具有将其指向特定元素的“源”属性。当然,我的实际源文档(和实际的人行横道)比这些例子更复杂和多变。

以下是源文档示例:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <album>
        <artist>Frank Sinatra</artist>
        <title>Greatest Hits</title>
    </album>
    <album>
        <artist>Miles Davis</artist>
        <title>Kind Of Blue</title>
    </album>
    <movie>
        <title>ET</title>
        <director>Steven Spielberg</director>
    </movie>
    <movie>
        <title>Blues Brothers</title>
        <director>John Landis</director>
    </movie>
</root>

这是“人行横道”(crswlk.xml):

<?xml version="1.0" encoding="UTF-8"?>

<root>
    <album>
        <artist-info>
            <artist2 source="artist"/>
        </artist-info>
    </album>
    <movie>
        <director-info>
            <director2 source="director"/>
        </director-info>
    </movie>
</root>

这是所需的输出:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <album>
        <artist>Frank Sinatra</artist>
        <title>Greatest Hits</title>
        <artist-info>
            <artist2>Frank Sinatra</artist2>
        </artist-info>
    </album>
    <album>
        <artist>Miles Davis</artist>
        <title>Kind Of Blue</title>
        <artist-info>
            <artist2>Miles Davis</artist2>
        </artist-info>
    </album>
    <movie>
        <title>ET</title>
        <director>Steven Spielberg</director>
        <director-info>
            <director2>Steven Spielberg</director2>
        </director-info>
    </movie>
    <movie>
        <title>Blues Brothers</title>
        <director>John Landis</director>
        <director-info>
            <director2>John Landis</director2>
        </director-info>
    </movie>
</root>

这是我的xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

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

    <xsl:template match="*/album|movie">
        <xsl:variable name="theNode" select="."/>
        <xsl:variable name="nodeName" select="name()"/>
        <xsl:element name="{$nodeName}">
            <xsl:apply-templates select="@* | node()"/>
            <xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">
                <xsl:with-param name="curNode" select="$theNode"/>
            </xsl:apply-templates>
        </xsl:element>
    </xsl:template>

    <xsl:template match="*[@source]">
        <xsl:param name="curNode" />
        <xsl:variable name="sourceNodeName" select="@source"/>
        <xsl:element name="{name()}">
            <xsl:value-of select="$curNode//*[name()=$sourceNodeName]"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

一旦最后一个模板第一次尝试访问$curNode,这将停止处理。当我在Eclipse(Xalan 2.7.1)中运行xslt时,它会抛出此错误:“java.lang.ClassCastException:org.apache.xpath.objects.XString无法强制转换为org.apache.xpath.objects.XNodeSet”。< / p>

如果我将类似的节点集作为参数传递给与源文档中的节点匹配的模板,它将按预期工作 - 可以访问节点集。但是,将节点集传递给上面的最后一个模板不起作用。是因为模板匹配外部文档中的节点吗?我当然不知道。任何帮助非常感谢,我花了一段时间才达到这一点。谢谢!

2 个答案:

答案 0 :(得分:4)

看起来你需要改变两件事:

  • tunnel="yes"添加到xsl:with-paramxsl:param
  • '$sourceNodeName'
  • 的谓词中删除xsl:value-of中的撇号

更新了XSLT:

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

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

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

    <xsl:template match="*/album|movie">
        <xsl:variable name="theNode" select="."/>
        <xsl:variable name="nodeName" select="name()"/>
        <xsl:element name="{$nodeName}">
            <xsl:apply-templates select="@* | node()"/>
            <xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">
                <xsl:with-param name="curNode" select="$theNode" tunnel="yes"/>
            </xsl:apply-templates>
        </xsl:element>
    </xsl:template>

    <xsl:template match="*[@source]">
        <xsl:param name="curNode" tunnel="yes"/>
        <xsl:variable name="sourceNodeName" select="@source"/>
        <xsl:element name="{name()}">
            <xsl:value-of select="$curNode//*[name()=$sourceNodeName]"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

此外,您可以删除一些额外的xsl:variables ...

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

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

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

    <xsl:template match="album|movie">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
            <xsl:apply-templates select="$crosswalk/*/*[name()=current()/name()]/*">
                <xsl:with-param name="curNode" select="." tunnel="yes"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@source]">
        <xsl:param name="curNode" tunnel="yes"/>
        <xsl:copy>
            <xsl:value-of select="$curNode//*[name()=current()/@source]"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:3)

如果您使用的是XSLT 2.0,那么Daniel Haley对隧道的回答肯定是要走的路。但是,如果您实际上使用Xalan,因此只使用XSLT 1.0,则需要采用不同的方法。

问题从这一行开始:

<xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">

这将在您的交叉步行文档中选择艺术家信息 director-info ,但您没有与这些匹配的特定模板,因此您使用的是通用身份模板使用将匹配他们

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

但是这不会带任何参数,也不会传递任何参数。因此,当您的上一个模板<xsl:template match="*[@source]">匹配时, curNode 将为空(一个空字符串),这不是一个节点集,并且令Xalan感到悲伤。

因此,要在XSLT1.0中解决此问题,只需将参数添加到身份模板中,然后将其传递:

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

如果模板在没有传递参数时匹配,它将只传递一个空参数而没有任何问题。

这是完整的XSLT(也可以从xsl:value-of中删除撇号的修正,如Daniel的回答中所述)。

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

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

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

    <xsl:template match="*/album|movie">
        <xsl:variable name="theNode" select="."/>
        <xsl:variable name="nodeName" select="name()"/>
        <xsl:element name="{$nodeName}">
            <xsl:apply-templates select="@* | node()"/>
            <xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">
                <xsl:with-param name="curNode" select="$theNode"/>
            </xsl:apply-templates>
        </xsl:element>
    </xsl:template>

    <xsl:template match="*[@source]">
        <xsl:param name="curNode" />
        <xsl:variable name="sourceNodeName" select="@source"/>
        <xsl:element name="{name()}">
            <xsl:value-of select="$curNode//*[name()=$sourceNodeName]"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

当应用于XML文档时,输出以下内容

<root>
   <album>
      <artist>Frank Sinatra</artist>
      <title>Greatest Hits</title>
      <artist-info>
         <artist2>Frank Sinatra</artist2>
      </artist-info>
   </album>
   <album>
      <artist>Miles Davis</artist>
      <title>Kind Of Blue</title>
      <artist-info>
         <artist2>Miles Davis</artist2>
      </artist-info>
   </album>
   <movie>
      <title>ET</title>
      <director>Steven Spielberg</director>
      <director-info>
         <director2>Steven Spielberg</director2>
      </director-info>
   </movie>
   <movie>
      <title>Blues Brothers</title>
      <director>John Landis</director>
      <director-info>
         <director2>John Landis</director2>
      </director-info>
   </movie>
</root>