嵌套html标记的XSL转换

时间:2013-11-20 12:00:17

标签: html xml xslt

我有一系列由Java应用程序输出的文档,这些文档使用未转义的html标记导出XML,例如

<b>some text</b>

(我不能改变这种行为)。

然后使用此输出的应用必须将所有html标记转义为

&lt;b&gt;some text &lt;/b&gt;

我使用下面的xslt来转义标签但不出意外它对嵌套 html标签不起作用,例如

<u><b>A string of html</b></u>

在XSLT转换后,我得到了

&lt;u&gt;a string of html&lt;/u&gt; 

where nested <b> and </b> tags get removed altogether.

我希望实现

&lt;u&gt;&lt;b&gt;A string of html&lt;/b&gt;&lt;/u&gt;

我确信通过调整select或模板的值可以轻松回答这个问题,但我已经尝试过并且失败了

非常感谢任何帮助!

带有嵌入式html标签的示例文档

<?xml version="1.0" encoding="UTF-8"?>
<Main>
<Text><u><b>A string of html</b></u></Text>
</Main>

这是XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="UTF-8"/>
<xsl:strip-space elements="*" />  

<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

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

</xsl:stylesheet>

哪个产生

<?xml version="1.0" encoding="UTF-8"?>
<Main>
  <Text>&lt;u&gt;A string of html&lt;/u&gt;</Text>
</Main>

您可以看到内部粗体标记已被删除。

任何人都可以帮助调整xslt吗?

谢谢: - )

2 个答案:

答案 0 :(得分:5)

尝试将当前的Text/*模板更改为此

<xsl:template match="Text//*">
  <xsl:value-of select="concat('&lt;',name(),'&gt;')" />
  <xsl:apply-templates />
  <xsl:value-of select="concat('&lt;/',name(),'&gt;')" />
</xsl:template>

因此,Text//*将匹配 Text 元素的任何后代元素,而不仅仅是直接子元素。然后分别输出打开和关闭模板,在这些模板之间递归调用模板来处理'嵌套'元素。

当应用于您的示例XML时,应输出以下内容

<Main>
  <Text>&lt;u&gt;&lt;b&gt;A string of html&lt;/b&gt;&lt;/u&gt;</Text>
</Main>

答案 1 :(得分:1)

由于您在评论中声明您尝试“转义”的代码可能具有属性,因此Tim C's solution不足。你真正需要的是更像这样的东西(因为你说有问题的标签是HTML我会假设你不需要关心命名空间):

<xsl:template match="Text//*">
  <xsl:value-of select="concat('&lt;',name())" />
  <xsl:apply-templates select="@*" mode="escape" />
  <xsl:text>&gt;</xsl:text>
  <xsl:apply-templates />
  <xsl:value-of select="concat('&lt;/',name(),'&gt;')" />
</xsl:template>

<xsl:template match="@*" mode="escape">
  <xsl:value-of select="concat(' ', name(), '=&quot;')" />
  <xsl:call-template name="doubleEscapeQuotes" />
  <xsl:text>"</xsl:text>
</xsl:template>

<xsl:template name="doubleEscapeQuotes">
  <xsl:param name="value" select="string(.)" />
  <xsl:choose>
    <xsl:when test="contains($value, '&quot;')">
      <xsl:value-of select="substring-before($value, '&quot;')" />
      <xsl:text>&amp;quot;</xsl:text>
      <xsl:call-template name="doubleEscapeQuotes">
        <xsl:with-param name="value" select="substring-after($value, '&quot;')" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$value" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

这会转换

<a title="An &quot;example&quot; website" href="http://example.com">link</a>

&lt;a title="An &amp;quot;example&amp;quot; website" href="http://example.com"&gt;link&lt;/a&gt;

但它仍然缺少一些必要的逻辑 - 你需要在属性值和两个属性值中双重逃避至少&符号(&amp;amp;)和小于(&amp;lt;)的符号。 在HTML元素的文本内容中,以便在未转义之后保持生成的标记格式良好(您可能还想逃避大于可读性的标记,但这并不像{{1}那样重要}和&)。

这比最初出现时要困难得多。 this question的各种答案可能会有所帮助。