如何在我的XSLT的HTML输出中发出节点的转义XML表示

时间:2010-04-14 14:50:18

标签: xslt

我正在将XML转换为HTML文档。在本文档中,我想为刚刚转换的节点嵌入XML标记(HTML文档是技术规范)。

例如,如果我的XML是这样的:

<transform-me>
    <node id="1">
        <stuff type="floatsam">
           <details>Various bits</details>
        </stuff>
    </node>
</transform-me>

我希望我的XSLT输出看起来像这样:

<h1>Node 1</h1>
   <h2>Stuff (floatsam)</h2>
   Various bits
   <h2>The XML</h2>
   &lt;stuff type=&quot;floatsam&quot;&gt;
      &lt;details&gt;Various bits&lt;/details&gt;
   &lt;/stuff&gt;

我希望有一个XSLT函数,我可以在我的&lt; stuff&gt;中调用它。我可以传递当前节点(。)的模板,并获取&lt; stuff&gt;的转义XML标记及其所有后代。我有一种感觉未解析的文本()可能是要走的路,但却无法让它发挥作用。

2 个答案:

答案 0 :(得分:8)

非常简单的模板

<xsl:template match="node()" mode="print">

        <xsl:choose>

            <!-- is it element? -->
            <xsl:when test="name()">

                <!-- start tag -->
                <xsl:text>&lt;</xsl:text>
                <xsl:value-of select="name()" />

                <!-- attributes -->
                <xsl:apply-templates select="@*" mode="print" />

                <xsl:choose>

                    <!-- has children -->
                    <xsl:when test="node()">
                        <!-- closing bracket -->
                        <xsl:text>&gt;</xsl:text>

                        <!-- children -->
                        <xsl:apply-templates mode="print" />

                        <!-- end tag -->
                        <xsl:text>&lt;/</xsl:text>
                        <xsl:value-of select="name()" />
                        <xsl:text>&gt;</xsl:text>
                    </xsl:when>

                    <!-- is empty -->
                    <xsl:otherwise>
                        <!-- closing bracket -->
                        <xsl:text>/&gt;</xsl:text>
                    </xsl:otherwise>

                </xsl:choose>

            </xsl:when>

            <!-- text -->
            <xsl:otherwise>
                <xsl:copy />
            </xsl:otherwise>

        </xsl:choose>

</xsl:template>

<xsl:template match="@*" mode="print">
    <xsl:text> </xsl:text>
    <xsl:value-of select="name()" />
    <xsl:text>=&quot;</xsl:text>
    <xsl:value-of select="." />
    <xsl:text>&quot;</xsl:text>
</xsl:template>

<强>二手

<xsl:apply-templates mode="print" />

如果需要,您甚至可以添加漂亮的打印。

答案 1 :(得分:7)

这个问题的答案比你最初想的要复杂得多。必须正确地“双重转义”属性值和文本节点。

这个XSLT 1.0模板对XML节点进行了正确的(虽然不是完整的)打印,包括(试图)使用可配置的缩进进行正确的漂亮打印:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="html" encoding="utf-8" />

   <!-- defaults and configurable parameters -->
  <xsl:param name="NL"        select="'&#xA;'" /><!-- newline sequence -->
  <xsl:param name="INDENTSEQ" select="'&#x9;'" /><!-- indent sequence -->

  <xsl:variable name="LT" select="'&lt;'" />
  <xsl:variable name="GT" select="'&gt;'" />

  <xsl:template match="transform-me">
    <html>
      <body>
        <!-- this XML-escapes an entire sub-structure -->
        <pre><xsl:apply-templates select="*" mode="XmlEscape" /></pre>
      </body>
    </html>
  </xsl:template>

  <!-- element nodes will be handled here, incl. proper indenting -->
  <xsl:template match="*" mode="XmlEscape">
    <xsl:param name="indent" select="''" />

    <xsl:value-of select="concat($indent, $LT, name())" />
    <xsl:apply-templates select="@*" mode="XmlEscape" />

    <xsl:variable name="HasChildNode" select="node()[not(self::text())]" />
    <xsl:variable name="HasChildText" select="text()[normalize-space()]" />
    <xsl:choose>
      <xsl:when test="$HasChildNode or $HasChildText">
        <xsl:value-of select="$GT" />
        <xsl:if test="not($HasChildText)">
          <xsl:value-of select="$NL" />
        </xsl:if>
        <!-- render child nodes -->
        <xsl:apply-templates mode="XmlEscape" select="node()">
          <xsl:with-param name="indent" select="concat($INDENTSEQ, $indent)" />
        </xsl:apply-templates>
        <xsl:if test="not($HasChildText)">
          <xsl:value-of select="$indent" />
        </xsl:if>
        <xsl:value-of select="concat($LT, '/', name(), $GT, $NL)" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="concat(' /', $GT, $NL)" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- comments will be handled here -->
  <xsl:template match="comment()" mode="XmlEscape">
    <xsl:param name="indent" select="''" />
    <xsl:value-of select="concat($indent, $LT, '!--', ., '--', $GT, $NL)" />
  </xsl:template>

  <!-- text nodes will be printed XML-escaped -->
  <xsl:template match="text()" mode="XmlEscape">
    <xsl:if test="not(normalize-space() = '')">
      <xsl:call-template name="XmlEscapeString">
        <xsl:with-param name="s" select="." />
        <xsl:with-param name="IsAttribute" select="false()" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <!-- attributes become a string: '{name()}="{escaped-value()}"' -->
  <xsl:template match="@*" mode="XmlEscape">
    <xsl:value-of select="concat(' ', name(), '=&quot;')" />
    <xsl:call-template name="XmlEscapeString">
      <xsl:with-param name="s" select="." />
      <xsl:with-param name="IsAttribute" select="true()" />
    </xsl:call-template>
    <xsl:value-of select="'&quot;'" />
  </xsl:template>

  <!-- template to XML-escape a string -->
  <xsl:template name="XmlEscapeString">
    <xsl:param name="s" select="''" />
    <xsl:param name="IsAttribute" select="false()" />
    <!-- chars &, < and > are never allowed -->
    <xsl:variable name="step1">
      <xsl:call-template name="StringReplace">
        <xsl:with-param name="s"       select="$s" />
        <xsl:with-param name="search"  select="'&amp;'" />
        <xsl:with-param name="replace" select="'&amp;amp;'" />
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="step2">
      <xsl:call-template name="StringReplace">
        <xsl:with-param name="s"       select="$step1" />
        <xsl:with-param name="search"  select="'&lt;'" />
        <xsl:with-param name="replace" select="'&amp;lt;'" />
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="step3">
      <xsl:call-template name="StringReplace">
        <xsl:with-param name="s"       select="$step2" />
        <xsl:with-param name="search"  select="'&gt;'" />
        <xsl:with-param name="replace" select="'&amp;gt;'" />
      </xsl:call-template>
    </xsl:variable>
    <!-- chars ", TAB, CR and LF are never allowed in attributes -->
    <xsl:choose>
      <xsl:when test="$IsAttribute">
        <xsl:variable name="step4">
          <xsl:call-template name="StringReplace">
            <xsl:with-param name="s"       select="$step3" />
            <xsl:with-param name="search"  select="'&quot;'" />
            <xsl:with-param name="replace" select="'&amp;quot;'" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="step5">
          <xsl:call-template name="StringReplace">
            <xsl:with-param name="s"       select="$step4" />
            <xsl:with-param name="search"  select="'&#x9;'" />
            <xsl:with-param name="replace" select="'&amp;#x9;'" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="step6">
          <xsl:call-template name="StringReplace">
            <xsl:with-param name="s"       select="$step5" />
            <xsl:with-param name="search"  select="'&#xA;'" />
            <xsl:with-param name="replace" select="'&amp;#xD;'" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="step7">
          <xsl:call-template name="StringReplace">
            <xsl:with-param name="s"       select="$step6" />
            <xsl:with-param name="search"  select="'&#xD;'" />
            <xsl:with-param name="replace" select="'&amp;#xD;'" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$step7" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$step3" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- generic string replace template -->
  <xsl:template name="StringReplace">
    <xsl:param name="s"       select="''" />
    <xsl:param name="search"  select="''" />
    <xsl:param name="replace" select="''" />

    <xsl:choose>
      <xsl:when test="contains($s, $search)">
        <xsl:value-of select="substring-before($s, $search)" />
        <xsl:value-of select="$replace" />
        <xsl:variable name="rest" select="substring-after($s, $search)" />
        <xsl:if test="$rest">
          <xsl:call-template name="StringReplace">
            <xsl:with-param name="s"       select="$rest" />
            <xsl:with-param name="search"  select="$search" />
            <xsl:with-param name="replace" select="$replace" />
          </xsl:call-template>
        </xsl:if>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$s" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

当应用于此测试XML时:

<transform-me>
  <node id="1">
    <!-- a comment -->
      <stuff type="fl&quot;&apos;&#10;&#9;oatsam">
        <details>Various bits &amp; pieces</details>
        <details>
        </details>
        <details attr="value">
          <childnode>text and &lt;escaped-text /&gt;</childnode>
        </details>
      </stuff>
  </node>
</transform-me>

生成以下输出(源代码):

<html>
<body>
<pre>&lt;node id="1"&gt;
    &lt;!-- a comment --&gt;
    &lt;stuff type="fl&amp;quot;'&amp;#xD;&amp;#x9;oatsam"&gt;
        &lt;details&gt;Various bits &amp;amp; pieces&lt;/details&gt;
        &lt;details /&gt;
        &lt;details attr="value"&gt;
            &lt;childnode&gt;text and &amp;lt;escaped-text /&amp;lt;&lt;/childnode&gt;
        &lt;/details&gt;
    &lt;/stuff&gt;
&lt;/node&gt;
</pre>
</body>
</html>

在浏览器中查看时,您会看到:

<node id="1">
    <!-- a comment -->
    <stuff type="fl&quot;'&#xD;&#x9;oatsam">
        <details>Various bits &amp; pieces</details>
        <details />
        <details attr="value">
            <childnode>text and &lt;escaped-text /&lt;</childnode>
        </details>
    </stuff>
</node>

请注意,“not complete”意味着名称空间和处理指令之类的内容目前尚未处理。但很容易为他们制作模板。