查看源码 类似 XSLT 的转换

示例


示例 1 使用 xslapply

原始 XSLT

<xsl:template match="doc/title">
    <h1>
      <xsl:apply-templates/>
    </h1>
</xsl:template>

在 Erlang 中变为

template(E = #xmlElement{ parents=[{'doc',_}|_], name='title'}) ->
    ["<h1>",
         xslapply(fun template/1, E),
     "</h1>"];


示例 2 使用 value_of 和 select

<xsl:template match="title">
  <div align="center"><h1><xsl:value-of select="." /></h1></div>
</xsl:template>

变为

template(E = #xmlElement{name='title'}) ->
    ["<div align=\"center\"><h1>", value_of(select(".", E)), "</h1></div>"];


示例 3 简单的 xsl 样式表

一个完整的示例,其中 XSLT 样式表在 xmerl 发行版中。

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

  <xsl:strip-space elements="doc chapter section"/>
  <xsl:output
    method="xml"
    indent="yes"
    encoding="iso-8859-1"
  />

  <xsl:template match="doc">
    <html>
      <head>
        <title>
          <xsl:value-of select="title"/>
        </title>
      </head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="doc/title">
    <h1>
      <xsl:apply-templates/>
    </h1>
  </xsl:template>

  <xsl:template match="chapter/title">
    <h2>
      <xsl:apply-templates/>
    </h2>
  </xsl:template>

  <xsl:template match="section/title">
    <h3>
      <xsl:apply-templates/>
    </h3>
  </xsl:template>

  <xsl:template match="para">
    <p>
      <xsl:apply-templates/>
    </p>
  </xsl:template>

  <xsl:template match="note">
    <p class="note">
      <b>NOTE: </b>
      <xsl:apply-templates/>
    </p>
  </xsl:template>

  <xsl:template match="emph">
    <em>
      <xsl:apply-templates/>
    </em>
  </xsl:template>

</xsl:stylesheet>


示例 4 Erlang 版本

前一个示例的 Erlang 转换

-include("xmerl.hrl").

-import(xmerl_xs,
    [ xslapply/2, value_of/1, select/2, built_in_rules/2 ]).

doctype()->
    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\
 \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd \">".

process_xml(Doc)->
    template(Doc).

template(E = #xmlElement{name='doc'})->
    [ "<\?xml version=\"1.0\" encoding=\"iso-8859-1\"\?>",
      doctype(),
      "<html xmlns=\"http://www.w3.org/1999/xhtml\" >"
      "<head>"
      "<title>", value_of(select("title",E)), "</title>"
      "</head>"
      "<body>",
      xslapply( fun template/1, E),
      "</body>"
      "</html>" ];


template(E = #xmlElement{ parents=[{'doc',_}|_], name='title'}) ->
    ["<h1>",
     xslapply( fun template/1, E),
     "</h1>"];

template(E = #xmlElement{ parents=[{'chapter',_}|_], name='title'}) ->
    ["<h2>",
     xslapply( fun template/1, E),
     "</h2>"];

template(E = #xmlElement{ parents=[{'section',_}|_], name='title'}) ->
    ["<h3>",
     xslapply( fun template/1, E),
     "</h3>"];

template(E = #xmlElement{ name='para'}) ->
    ["<p>", xslapply( fun template/1, E), "</p>"];

template(E = #xmlElement{ name='note'}) ->
    ["<p class=\"note\">"
     "<b>NOTE: </b>",
     xslapply( fun template/1, E),
     "</p>"];

template(E = #xmlElement{ name='emph'}) ->
    ["<em>", xslapply( fun template/1, E), "</em>"];

template(E)->
    built_in_rules( fun template/1, E).

如果您希望在“推送”转换中写入任何文本,则必须以调用 xmerl_xs:built_in_rules/2 结束。这些是大量使用 xslapply( fun template/1, E ) 而不是 value_of(select("xpath",E)) 的转换,后者是拉取...


最大的示例是将本文档从简化 Docbook XML 格式转换为 xhtml 的样式表。源文件是 sdocbook2xhtml.erl。

技巧和窍门

for-each

for-each 函数在 XSLT 样式表中非常常见。它通常可以重写并替换为 select/1。由于 select/1 返回一个 #xmlElements 列表,而 xslapply/2 遍历它们,因此它或多或少等同于循环遍历所有元素。

position()

XSLT 的 position() 和 #xmlElement.pos 不是相同的。必须在 Erlang 中创建自己的位置。


示例 5 计算位置

<xsl:template match="stanza">
  <p><xsl:apply-templates select="line" /></p>
</xsl:template>

<xsl:template match="line">
  <xsl:if test="position() mod 2 = 0">&#160;&#160;</xsl:if>
  <xsl:value-of select="." /><br />
</xsl:template>

可以写成

template(E = #xmlElement{name='stanza'}) ->
    {Lines,LineNo} = lists:mapfoldl(fun template_pos/2, 1, select("line", E)),
    ["<p>", Lines, "</p>"].

template_pos(E = #xmlElement{name='line'}, P) ->
    {[indent_line(P rem 2), value_of(E#xmlElement.content), "<br />"], P + 1 }.

indent_line(0)->"&#160;&#160;";
indent_line(_)->"".

全局树感知

在 XSLT 中,您可以使用 XPath 对树的顶部进行“根”访问,即使您位于树的深处。

xslapply/2 函数仅将树的子部分带回模板 fun。但是,编写可以同时处理子树和顶层树的模板 fun 非常容易。


示例 6 传递根树

以下示例片段会将文章标题添加到任何节标题之前

template(E = #xmlElement{name='title'}, ETop ) ->
    ["<h3>", value_of(select("title", ETop))," - ",
     xslapply( fun(A) -> template(A, ETop) end, E),
     "</h3>"];