XSLT 中用 JavaScript 自定义函数处理参数

曾经写过一篇 XSLT 文件中使用C#/JScript/VB 自定义函数 怎么用 C#/JScript/VB 在 XSL/XSLT 中自定义函数。那时候原本想直接用 JavaScript 来自定义函数,因为 JavaScript 哪台机器上都能跑,但出了些问题,所以实际中是用的 C# 自定义的 XSLT 函数。


这样的问题无论从哪方面讲都一直回避不了,情况是如果在 JavaScript 定义函数中直接把传入的参数返回是没问题的,但要作任何的处理,或者调用 JS 函数都会失败。XSLT 中的自定义函数,不对参数进行加工处理是没有意义的。比如在 XSLT 中写如下函数:
 1<msxsl:script implements-prefix="myfn" language="JavaScript">
 2    function show(blogUrl){
 3        return "".blogUrl;
 4
 5        //return blogUrl.split(":")[1]; //如果是对它调用一个方法
 6    }
 7</msxsl>
 8
 9<!-- 在模板中这么调用 -->
10<xsl:value-of select="myfn:show(blogUrl)"/>

其实上面那个 show 也是什么事都没做,除非是 return blogUrl; 否则就会报错:

Function 'show' did not return a value, or it returned a value that cannot be converted to an XSL data type.

如果执行的是第二行代码提示的错误将会是:

Microsoft JScript runtime error Object doesn't support this property or method line = 18, col = 3 (line is offset from the s...

出错那时我也没太明白过来是什么原因出错的,因为我也是想当然的认为传入的参数是个字符串,其实不然,若用 typeof(blogUrl) 看下就知道它总是个 object。因为不光是文本节点会作为参数,其他的节点也可能放进来,又因为 C# 是强类型的,因而在用 C# 自定义 XSLT 函数时幸免了。

解决办法是,必须指明类型,即调时的代码的 myfn:show(blogUrl) 必须写成 myfn:show(string(blogUrl))。如果是数字类型就用 number(level)。严格说来 string() 和 number() 并不是在做类型转换,它们都是XPATH 中的函数,即默认为 fn 命名空间下的函数。你也可以试下别的,不过一般我们在自定义函数中处理那些文本或数字的就足矣,笼统来说用 string() 先全部得到对应的字符串就行,只要其中的数据不丢失即可。

最后也完整的补个例子吧,在 xml 中指定一个 xstl 文件,这样就可以直接在 IE 中浏览到转换后的效果,而且也只能在微软的 IE 出成果,大约是用了 xmlns:msxsl="urn:schemas-microsoft-com:xslt" 这样非规范的东西。

text.xml
 1<?xml version="1.0" encoding="utf-8"?>
 2<?xml-stylesheet type="text/xsl" href="test.xsl"?>
 3
 4<persons>
 5 <person>
 6  <name>Unmi</name>
 7  <level>9</level>
 8  <registerTime>8/3/2010 11:17:34</registerTime>
 9  <blogUrl>http://unmi.cc</blogUrl>
10 </person>
11 <person>
12  <name>Fantasia</name>
13  <level>5</level>
14  <blogUrl>http://scalaworks.com</blogUrl>
15  <registerTime>9/3/2009 14:17:34</registerTime>
16 </person>
17</persons>

test.xsl
 1<?xml version="1.0" encoding="utf-8"?>
 2<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 3    xmlns:myfn="http://unmi.cc/fn"
 4    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl myfn">
 5
 6   <msxsl:script implements-prefix="myfn" language="JavaScript">
 7    <![CDATA[
 8        function handleTitle(name,level){
 9   //即使是简单的链接,传入前也必须用 string() 指明参数类型;
10   //就是像 return ''.name; 都要指明类型;
11   return name+" : "+level;
12        }
13
14     function transDate(registerTime){
15     //return typeof(registerTimte);//如果未指定,则为 object
16           var dateArr = registerTime.split(' ');
17           var darr = dateArr[0].split('/');
18           return darr[2]+'-'+darr[0]+'-'+darr[1]+" "+dateArr[1];
19     }
20
21  function show(blogUrl){
22      //只有直接返回参数时才无需指定参数类型
23      return blogUrl;
24  }
25    ]]>
26    </msxsl:script>
27
28    <xsl:output method="html" indent="yes"/>
29
30    <xsl:template match="/persons">
31        <table border="1">
32   <thead><th>Name</th><th>Register Time</th><th>Blog URL</th></thead>
33         <xsl:apply-templates select="person"/>
34     </table>
35    </xsl:template>
36    <xsl:template match="person">
37     <tr>
38         <td><xsl:value-of select="myfn:handleTitle(string(name),number(level))"/></td>
39      <td><xsl:value-of select="myfn:transDate(string(registerTime))"/></td>
40      <td><xsl:value-of select="myfn:show(blogUrl)"/></td>
41     </tr>
42 </xsl:template>
43</xsl:stylesheet>

参考: 1. XPath、XQuery 以及 XSLT 函数
        2. XML XSLT parameters Javascript - how to make it work in Mozilla...
        3. W3Schools Forum > XML Forums > XSLT 永久链接 https://yanbin.blog/xslt-javascript-function-parameter/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。