1

我想创建一个通用的 XSLT 2.0 函数,它可以像这样在 xml 上运行:

<?xml version="1.0"?>
<foo xmlns:my="http://meohmy.org" xmlns:yours="http://meohmy2.org">
    <yours:pet my:type="dog" my:color="red">Cindy</yours:pet>
    <yours:pet my:type="cat">Felix</yours:pet>
    <yours:pet my:type="cat" my:color="green">Pai Mei</yours:pet>
</foo> 

并给出这样的函数签名:

my:doit('item',/foo/yours:/pet,@my:color)

(第一个参数是标签,第二个是我要报告的节点集,第三个是我想为第二个参数的节点集中的每个节点输出的值,无论它是否有)。

我希望它返回这样的数据:

info=red;;green

请注意,我想要一个与没有第三个参数的元素对应的空占位符,并且顺序很重要。

由于我在样式表中经常这样做(出于不同原因数十次),因此函数似乎是一种自然的方式。这是我到目前为止想出的...

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:my="http://meohmy.org" 
    xmlns:yours="http://meohmy2.org">

   <xsl:template match="/">
       <xsl:value-of select="my:doit('item',/foo/yours:pet,@my:color)"/>
   </xsl:template>

   <xsl:function name="my:doit" as="xs:string?">
      <xsl:param name="item" as="xs:string"/>
      <xsl:param name="value"/>
      <xsl:param name="subvalue"/>
      <xsl:if test="$value">
        <xsl:value-of>
             <xsl:value-of select="$item"/>
             <xsl:text>=</xsl:text>
             <xsl:for-each select="$value">
                  <xsl:value-of select="current()/$subvalue"/>
              <xsl:if test="position() != last()">
                   <xsl:text>;</xsl:text>
              </xsl:if>
             </xsl:for-each>
        </xsl:value-of>
    </xsl:if>
  </xsl:function>
</xsl:stylesheet>

但是,当我这样做时,我从函数 doit() 中得到以下值:

item=;;

这向我表明<xsl:value-of select="current()/$subvalue"/>样式表中的 不正确。我很接近,我能感觉到;> 有什么建议吗?

谢谢!

4

2 回答 2

1

在您的示例中,您认为您将 XPath 表达式作为第三个参数传递,但您不是 - 您正在传递它的值。

在 XPath 3.0 中有一个干净的解决方案来解决这个问题:您可以传递一个函数作为第三个参数。所以调用变成了

select="my:doit('item', /foo/yours:pet, function($e){$e/@my:color})"

并且实现变成

      <xsl:function name="my:doit" as="xs:string?">
          <xsl:param name="item" as="xs:string"/>
          <xsl:param name="value" as="element()*/>
          <xsl:param name="subvalue" as="function(element()) as xs:string?"/>
          <xsl:if test="$value">
            <xsl:value-of>
                 <xsl:value-of select="$item"/>
                 <xsl:text>=</xsl:text>
                 <xsl:value-of select="$value ! $subvalue(.)" separator=";"/>
            </xsl:value-of>
        </xsl:if>
      </xsl:function>

如果您不能使用 XPath 3.0(上述方法适用于 Saxon-PE 9.5),那么替代方案是(a)使用 Dimitre Novatchev 的 FXSL 库,它模拟 XSLT 2.0 中的高阶函数,或者(b)而不是传递一个函数,而是传递一个字符串形式的 XPath 表达式,并使用 xx:eval() 扩展函数来动态评估表达式 - 许多处理器提供这样的扩展,但它不是标准语言。

(注意,在 XPath 3.0 中,A!B 的含义与“for $x in A return $x/B”基本相同)

于 2013-06-26T08:23:30.027 回答
1

试试这个样式表

    <?xml version="1.0"?>
    <xsl:stylesheet version="2.0" 
        xmlns:xs="http://www.w3.org/2001/XMLSchema" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        xmlns:my="http://meohmy.org" 
        xmlns:yours="http://meohmy2.org">

       <xsl:template match="/">
           <xsl:value-of select="my:doit('item',/foo/yours:pet,'my:color')"/>
       </xsl:template>

       <xsl:function name="my:doit" as="xs:string?">
          <xsl:param name="item" as="xs:string"/>
          <xsl:param name="value"/>
          <xsl:param name="subvalue"/>
          <xsl:if test="$value">
            <xsl:value-of>
                 <xsl:value-of select="$item"/>
                 <xsl:text>=</xsl:text>
                 <xsl:for-each select="$value">
                      <xsl:value-of select="current()/attribute()[name() = $subvalue]"/>
                  <xsl:if test="position() != last()">
                       <xsl:text>;</xsl:text>
                  </xsl:if>
                 </xsl:for-each>
            </xsl:value-of>
        </xsl:if>
      </xsl:function>
    </xsl:stylesheet>

它给了我结果

item=red;;green
于 2013-06-26T06:09:18.887 回答