XSLTForms/转换函数
transform()
函数是 XSLTForms 对标准 XForms 函数库的扩展。公开讨论表明它是基于对 类似函数 的提议,该函数将被包含在 XForms 2.0 中,但工作似乎在 2011 年陷入停滞。在 XPath 表达式模块 的当前草案中似乎没有 transform()
函数,目前也没有为 transform()
函数单独模块的活动规范。
transform()
函数将外部 XSLT 样式表应用于节点集。在当前版本的 XSLTForms(大约从 2014 年开始)中,它接受三个或更多参数。
- 第一个参数是要转换的节点(例如
instance('foo')/bar/baz
或只是child-element
或.
)。 - 第二个参数要么是
- 作为字符串的 XSLT 样式表的 URI(例如
'my-transform.xsl'
),要么是 - XSLT 样式表的字符串表示。
- 作为字符串的 XSLT 样式表的 URI(例如
- 第三个参数如果是字符串形式的 XSLT 样式表,则为
true
,如果是 XSLT 样式表的 URI,则为false
。 - 如果存在,第四、六、八... 个参数是 XSLT 样式表参数的名称。
- 如果存在,第五、七、九... 个参数是这些参数的值。
注意:早期版本的函数只接受两个参数:要转换的节点和 XSLT 样式表的 URI。
transform()
函数返回一个字符串。这对许多 XSLT 用户来说似乎不太可能,因此需要强调:transform()
函数返回一个字符串。
- 如果 XSLT 输出方法是
text
,则返回值是字符串(在几乎所有浏览器中,在几乎所有情况下,都包装在某种浏览器相关的 XML 元素中)。 - 如果 XSLT 输出方法是
xml
,则返回值是生成的 XML 文档的序列化形式。在大多数情况下,查看它将显示尖括号和与号作为<
和&
进行转义。但是,如果返回值被注入到 XHTML 文档中,则结果将像普通的 HTML 一样格式化。
(那么 html
输出方法呢?)
transform()
的一个用途是将 XML 实例数据格式化为适当地格式化的 HTML,以便浏览器以方便的只读方式显示。例如
<div style="margin: 1em 0;">
<xf:output value="transform(my-tricky-element, 'display-tricky.xsl')"
mediatype="application/xhtml+xml" />
</div>
transform()
的另一个用途是美化 XML 实例数据,以便浏览器以方便的只读方式显示 XML 源代码。例如
<h2>XML representation of the data</h2>
<pre style="border: 2px #552211 solid;
padding: 0.5em;
background-color: #f7eed4;">
<xf:output value="transform(instance('doc'),'../lib/prettyprint.xsl')"
mediatype="text/plain" />
</pre>
此示例假设一个美化样式表,该样式表会生成带有换行符和适当缩进的输出。这在原则上不是必需的(可以使用 serialize(instance('user-data'))
代替),但在实践中很有用,因为否则一些浏览器会在一行很长的输出中显示整个 XML 文档。
由于 transform()
返回一个字符串,因此以下尝试为 XForms 生成新的文档实例以便进一步处理将失败:它不会用转换的输出替换实例 foo
,而是用转换结果的序列化字符串形式替换 foo
的内容。
<xf:trigger>
<xf:label>Generate diagram</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="instance('svg')"
value="transform(instance('input'),'make-svg.xsl')"/>
<xf:toggle case="picture"/>
</xf:action>
</xf:trigger>
如果 svg
实例最初包含一个正常的 SVG 文档,那么在触发显示的触发器后,它将包含一个 SVG 元素,该元素包含 XML 转义的 SVG 文档作为一个单独的文本节点。
<svg xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"><svg xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="900"
height="1000">
<desc>SVG representation of the data</desc>
...
</svg></svg>
解决此问题的一种方法是使用实验性动作 xf:setnode
以及 inner
或 outer
属性,以替换节点的内容或节点本身。
<xf:trigger>
<xf:label>Inner</xf:label>
<xf:setnode ref="." ev:event="DOMActivate"
inner="'<item>b</item>'"/>
</xf:trigger>
<xf:trigger>
<xf:label>Outer</xf:label>
<xf:setnode ref="item" ev:event="DOMActivate"
outer="'<item>c</item>'"/>
</xf:trigger>
另一种解决方法是将字符串提交到服务器上的脚本,该脚本将它作为 XML 回显。(参见 关于 setnode
操作的讨论 中的示例)。
在 Safari 中,以及在较旧版本的 Chrome 中(直到版本 30,甚至可能更晚),如果在 transform()
的第二个参数中给出的样式表包含 xsl:import
指令,则转换可能会失败。症状是一个错误警报,提示 TypeError: null is not an object (evaluating 'resultDocument.documentElement')
。(如果样式表包含 xsl:include
指令,则也可能会出现此问题,但尚未得到验证。)此问题不会出现在 Firefox、Opera 或更新版本的 Chrome 中(从版本 49 开始,甚至可能更早)。
解决方法:删除 xsl:import
,以便样式表成为一个独立的对象,不依赖于其他样式表。
可以从 一组 transform() 测试用例 中收集到一些进一步的信息,这些测试用例说明了调用函数的各种不同方法,并显示了在每种情况下它产生的结果。