嵌套的HTML标签的XSL转换

5

我有一系列由Java应用程序输出的文档,它导出了XML,并且未转义HTML标签。例如:

<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;

我相信通过调整value-of 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吗?

谢谢 :-)


1
对于一个清晰且结构合理的问题,给予+1。 - Mathias Müller
1
你想要“转义”的标签中是否有属性(例如 <a href="http://example.com">link</a>)? - Ian Roberts
是的,他们可以,但这已经在Tim C的解决方案中得到涵盖。无论如何,感谢您的回答! - user3012857
2个回答

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>

谢谢Tim,你真是个明星。这太棒了,如此简单而且显然,只要你知道!我忘记了XPath语法。祝编码愉快 :-) - user3012857

1

由于您在评论中提到您尝试“转义”的标签可能具有属性,因此Tim C的解决方案是不够的。实际上,您需要类似于以下内容的更多东西(由于您说问题中的标签是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 元素的文本内容中,在解除转义后保持生成的标记格式正确(你可能也想为了可读性而转义大于号,但这不像 &< 那样重要)。
这比起初看起来更加困难。this question 的各种回答可能会有所帮助。

Ian,你当然是对的,谢谢你的更新。幸运的是,我发现所有的HTML标签都不会有属性,而且你再次正确地指出这比一开始看到的要复杂得多。真希望我能接受两个答案! - user3012857

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接