首页 > 解决方案 > 如何在 xsl:comment 中包含 xml 并保持缩进

问题描述

我正在编写一些 xsl 来将 xml 中的书籍大纲转换为一堆单独的 xml 文件(每章一个,加上一些前题等)。我将使用 来执行此操作<exsl:document>,并且单个文件的大部分内容将写入 xsl 中。

我使用 xsltproc,所以 xslt 1.0。

我想要可以使用的文本注释,还想要<xsl:comment>一些“注释掉”的 xml。正如这个问题中提到的,这是不可能使用的<xsl:comment>

该问题的答案用于<xsl:text disable-output-escaping="yes">&lt;!--</xsl:text>包装注释的 xml。这有效,只是一旦添加它,输出就不再正确缩进。

例如,下面的 xsl:

<?xml version='1.0' encoding="UTF-8"?>

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

<xsl:template match="/">
<root>
  <xsl:comment>Text comment</xsl:comment>
  <child><name>A child</name></child>
  <xsl:text disable-output-escaping="yes">&lt;!--</xsl:text>
  <child><name>commented child</name></child>
  <xsl:text disable-output-escaping="yes">--&gt;</xsl:text>
</root>
</xsl:template>

</xsl:stylesheet>

给出评论中的 xml,但没有缩进:

<root><!--Text comment--><child><name>A child</name></child><!--<child><name>commented child</name></child>--></root>

在使用这个时:

<xsl:template match="/">
<root>
  <xsl:comment>Text comment</xsl:comment>
  <child><name>A child</name></child>
  <xsl:comment><child><name>commented child</name></child></xsl:comment>
</root>
</xsl:template>

提供了很好的缩进,但评论中没有 xml 标签:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <!--Text comment-->
  <child>
    <name>A child</name>
  </child>
  <!--commented child-->
</root>

有没有办法保持缩进但将 xml 代码放在注释中?

标签: xmlxsltxslt-1.0

解决方案


这是一个可用于注释掉任何 XML 节点或片段的转换

  1. 使用DOE
  2. 保留缩进
  3. 甚至尝试表达嵌套的评论——在评论遇到评论时不会忽略评论或抛出错误:

==============================

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text" indent="yes"/>

  <xsl:template match="/">
    <xsl:value-of select="'&lt;!--&#xA;'"/>
      <xsl:apply-templates select="*" mode="escapeToText"/>
    <xsl:value-of select="'&#xA;-->'"/>
  </xsl:template>


  <xsl:template match="*" mode="escapeToText">
    <xsl:value-of select="concat('&lt;', name())"/>

    <xsl:apply-templates select="@*" mode="escapeToText"/>
    <xsl:value-of select="'>'"/>

    <xsl:apply-templates select="node()" mode="escapeToText"/>
    <xsl:value-of select="concat('&lt;/', name(), '>')"/>
  </xsl:template>

  <xsl:template match="@*" mode="escapeToText">
    <xsl:value-of select="concat(' ', name(), '=&quot;')"/>
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="string(.)"/>
    </xsl:call-template>
    <xsl:value-of select="'&quot;'"/>
  </xsl:template>

  <xsl:template match="text()" mode="escapeToText">
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="processing-instruction()" mode="escapeToText">
    <xsl:value-of select="concat('&lt;?', name(), ' ', ., ' ?>')"/>
  </xsl:template>

  <xsl:template match="comment()">
    <xsl:value-of select="concat('&lt;!CM ', ., 'CM>')"/>
  </xsl:template>

  <xsl:template name="escapeDoubleDash">
   <xsl:param name="pText"/>

   <xsl:choose>
     <xsl:when test="contains($pText, '--')">
       <xsl:value-of select="substring-before($pText, '--')"/>
       <xsl:value-of select="'!-!-'"/>
       <xsl:call-template name="escapeDoubleDash">
         <xsl:with-param name="pText" select="substring-after($pText, '--')"/>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise><xsl:value-of select="$pText"/></xsl:otherwise>
   </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

当此转换应用于任何源 xml 文档时,例如:

<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>

产生了想要的缩进结果

<!--
<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>
-->

下面是一个被注释掉的、非常复杂的 XML 文档的样子——让我们将这个转换应用到 ... 本身。

为了完整起见,我在模板匹配属性之前添加了注释和处理指令:

  <?somePI x="y" z="t" pqr ?>

  <!-- A Comment node -->

结果和预期的一样:

<!--
<xsl:stylesheet version="1.0">
 <xsl:output method="text" indent="yes"></xsl:output>

  <xsl:template match="/">
    <xsl:value-of select="'<!!-!-
'"></xsl:value-of>
      <xsl:apply-templates select="*" mode="escapeToText"></xsl:apply-templates>
    <xsl:value-of select="'
!-!->'"></xsl:value-of>
  </xsl:template>


  <xsl:template match="*" mode="escapeToText">
    <xsl:value-of select="concat('<', name())"></xsl:value-of>

    <xsl:apply-templates select="@*" mode="escapeToText"></xsl:apply-templates>
    <xsl:value-of select="'>'"></xsl:value-of>

    <xsl:apply-templates select="node()" mode="escapeToText"></xsl:apply-templates>
    <xsl:value-of select="concat('</', name(), '>')"></xsl:value-of>
  </xsl:template>

  <?somePI x="y" z="t" pqr  ?>

  <!CM  A Comment node CM>
  <xsl:template match="@*" mode="escapeToText">
    <xsl:value-of select="concat(' ', name(), '="')"></xsl:value-of>
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="string(.)"></xsl:with-param>
    </xsl:call-template>
    <xsl:value-of select="'"'"></xsl:value-of>
  </xsl:template>

  <xsl:template match="text()" mode="escapeToText">
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="."></xsl:with-param>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="processing-instruction()" mode="escapeToText">
    <xsl:value-of select="concat('<?', name(), ' ', ., ' ?>')"></xsl:value-of>
  </xsl:template>

  <xsl:template match="comment()">
    <xsl:value-of select="concat('<!CM ', ., 'CM>')"></xsl:value-of>
  </xsl:template>

  <xsl:template name="escapeDoubleDash">
   <xsl:param name="pText"></xsl:param>

   <xsl:choose>
     <xsl:when test="contains($pText, '!-!-')">
       <xsl:value-of select="substring-before($pText, '!-!-')"></xsl:value-of>
       <xsl:value-of select="'!-!-'"></xsl:value-of>
       <xsl:call-template name="escapeDoubleDash">
         <xsl:with-param name="pText" select="substring-after($pText, '!-!-')"></xsl:with-param>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise><xsl:value-of select="$pText"></xsl:value-of></xsl:otherwise>
   </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
-->

更新

如果您希望注释结果显示为 XML 文档的一部分——也就是说,不具有<xsl:output method="text"/>,我建议添加一个特殊元素,例如<MyComment>并将注释结果生成为 的文本节点子节点<MyComment>。一般情况下,被注释的 xml 片段会出现特殊字符转义。

但是有一个技巧——使用 CDATA 部分——然后你会看到文本未转义。这在一个例子中得到了最好的体现:

更新后的转换与上面提供的几乎相同,但是:

  1. 指令method="text"中没有<xsl:output>
  2. 注释文本是<MyComment>元素的文本节点
  3. 所有“注释 XML”——实际上表示为文本出现在 CDATA 部分中并且未转义

这是修改后的转换

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output indent="yes" cdata-section-elements="MyComment"/>

  <xsl:template match="/">
   <MyComment>
     <xsl:value-of select="'&lt;!--&#xA;'"/>
       <xsl:apply-templates select="*" mode="escapeToText"/>
     <xsl:value-of select="'&#xA;-->'"/>
    </MyComment>
  </xsl:template>


  <xsl:template match="*" mode="escapeToText">
    <xsl:value-of select="concat('&lt;', name())"/>

    <xsl:apply-templates select="@*" mode="escapeToText"/>
    <xsl:value-of select="'>'"/>

    <xsl:apply-templates select="node()" mode="escapeToText"/>
    <xsl:value-of select="concat('&lt;/', name(), '>')"/>
  </xsl:template>

  <xsl:template match="@*" mode="escapeToText">
    <xsl:value-of select="concat(' ', name(), '=&quot;')"/>
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="string(.)"/>
    </xsl:call-template>
    <xsl:value-of select="'&quot;'"/>
  </xsl:template>

  <xsl:template match="text()" mode="escapeToText">
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="processing-instruction()" mode="escapeToText">
    <xsl:value-of select="concat('&lt;?', name(), ' ', ., ' ?>')"/>
  </xsl:template>

  <xsl:template match="comment()">
    <xsl:value-of select="concat('&lt;!CM ', ., 'CM>')"/>
  </xsl:template>

  <xsl:template name="escapeDoubleDash">
   <xsl:param name="pText"/>

   <xsl:choose>
     <xsl:when test="contains($pText, '--')">
       <xsl:value-of select="substring-before($pText, '--')"/>
       <xsl:value-of select="'!-!-'"/>
       <xsl:call-template name="escapeDoubleDash">
         <xsl:with-param name="pText" select="substring-after($pText, '--')"/>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise><xsl:value-of select="$pText"/></xsl:otherwise>
   </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

当我们将此转换应用于以下 XML 文档时

<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>

产生了想要的、未转义的评论:

<MyComment><![CDATA[<!--
<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>
-->]]></MyComment>

我们可以去掉 XML 注释 ( <!-- -->) 和处理“嵌套注释”的代码——这不再需要。


推荐阅读