如何在XSLT中处理嵌入的XML标签?

14

我正在使用XSLT将XML转换为HTML。我在处理嵌入式XML节点以进行格式化时遇到了一些问题。例如,假设我有以下XML元素:

<favoriteMovie>the <i>Star Wars</i> saga</favoriteMovie>

然而,在XSLT过程中,<i>标签被忽略了,因此在HTML输出中,“Star Wars”没有被设置为斜体。是否有相对简单的方法来解决这个问题?

test.xml:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="test.html.xsl"?>
<favoriteMovies>
    <favoriteMovie>the <i>Star Wars</i> saga</favoriteMovie>
</favoriteMovies>

test.html.xsl:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" omit-xml-declaration="yes" />
    <xsl:template match="/">
      <html>
        <head />
          <body>
            <ul>
                <xsl:for-each select="favoriteMovies/favoriteMovie">
                    <li><xsl:value-of select="." /></li>
                </xsl:for-each>
            </ul>
          </body>
      </html>
    </xsl:template>
</xsl:stylesheet>

1
好问题,+1。请看我的答案,了解问题的原因和两个完整而简短的解决方案。请注意,目前被接受的答案是完全错误的。将标记放入CDATA部分会将其转换为无法使用的一维文本,并通常被认为是一种不良实践。 - Dimitre Novatchev
你不能声称i不属于favoriteMovies,因为任何地方都没有涉及命名空间或模式。这只是一种假设。如果favoriteMovies不包含其内容的定义,则i元素与favoriteMovie一样有效。如果原始XML有一个命名空间并且i未包含在其中,则正确的方法是处理命名空间,而不是将XML格式化文本作为字符串处理。 - James Walford
@etranger:仅仅因为你不得不使用DOE,就表明你已经创造了一堆需要使用DOE来清理的混乱。你是否知道DOE并不是XSLT的强制性功能,并且被所有XSLT处理器支持——最显著的例子是FireFox使用的处理器?在XSLT开发中,避免使用DOE除非没有其他解决方案是一个已经确立的好原则。请阅读并学习。 - Dimitre Novatchev
@Dimitre Novatchev,我只希望您能在不做出快速判断的情况下关注我的评论。您还没有回答我的有关破坏标记的问题,而是跳到了DOE不受所有处理器支持的争论上,而这与讨论无关。命名空间比DOE更好,我同意这一点,但如果可以的话,请不要称呼我为无知。 - Dennis Kreminsky
@James Walford,同意起点。我还建议作者为favoriteMovies结构使用单独的命名空间,以防将来出现问题。 - Dennis Kreminsky
显示剩余12条评论
4个回答

12
然而,在进行XLST转换时,<i>标签被忽略了,所以在HTML输出中,“Star Wars”没有斜体。是否有相对简单的方法可以解决这个问题?

你的问题在这里:

<ul>
  <xsl:for-each select="favoriteMovies/favoriteMovie">
    <li><xsl:value-of select="."/></li>
  </xsl:for-each>
</ul>
<xsl:value-of>指令用于创建文本节点。它通过复制在XSLT指令的select属性中指定的XPath表达式的字符串值,将其输出到输出中。元素的字符串值是其所有文本节点后代的串联。
因此,这就是如何获得所报告的输出。 解决方法: 使用<xsl:copy-of>指令,它会复制其select属性中指定的所有节点。
<ul>
  <xsl:for-each select="favoriteMovies/favoriteMovie">
    <li><xsl:copy-of select="node()"/></li>
  </xsl:for-each>
</ul>

另一种更符合XSLT原则的解决方案完全避免使用<xsl:for-each>

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

 <xsl:template match="/">
  <html>
    <head />
    <body>
     <xsl:apply-templates/>
    </body>
  </html>
 </xsl:template>

 <xsl:template match="/*">
  <ul>
   <xsl:apply-templates/>
  </ul>
 </xsl:template>

 <xsl:template match="favoriteMovie">
  <li><xsl:copy-of select="node()"/></li>
 </xsl:template>
</xsl:stylesheet>

当应用上述两种解决方案之一于提供的XML文档时:

<favoriteMovies>
    <favoriteMovie>the 
        <i>Star Wars</i> saga
    </favoriteMovie>
</favoriteMovies>

期望的、正确的结果已被生成:

<html>
    <head/>
    <body>
        <ul>
            <li>the 
                <i>Star Wars</i> saga
            </li>
        </ul>
    </body>
</html>

我更喜欢这个答案,因为它更简单,并且不会破坏我的纯文本XSLT。谢谢! - Craig W
@James-Walford:你的回答有一个问题:你无法预先知道favoriteMovie中会有哪些HTML元素,因此提供匹配任何可能的HTML元素的单独模板是不现实的。如果你真的想避免使用xsl:copy-of,你需要使用身份规则。我没有提供身份规则的解决方案,因为这距离原始问题太远了。 - Dimitre Novatchev
我应该能够弄清楚这个问题,但如果我想做同样的事情并且我始终只有一个节点而不是多个,那么XSL语法会是什么样子?我尝试将<xsl:value-of select="title" />更改为<xsl:copy-of select="title" />,但是< title >标记会显示在原始HTML中,它无法正确呈现。 - Craig W
@Dimitre Novatchev 谢谢 - 很好的观点,我的回答是基于只有 i 个元素的情况。 - James Walford
明白了:<xsl:copy-of select="title/node()" /> - Craig W

2
你应该使用xsl:copy来复制i节点。 http://msdn.microsoft.com/en-us/library/ms256128.aspx
    <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" omit-xml-declaration="yes" />
    <xsl:template match="/">
      <html>
        <head />
          <body>
            <xsl:apply-templates></xsl:apply-templates>        
          </body>
      </html>
    </xsl:template>
<xsl:template match="favoriteMovies">
  <ul>
    <xsl:apply-templates></xsl:apply-templates>
  </ul>  
</xsl:template>
  <xsl:template match="favoriteMovie">
    <li>      
      <xsl:apply-templates></xsl:apply-templates>
    </li>
  </xsl:template>
  <xsl:template match="i">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

通常来说,这是一种错误的做法,因为<i>不来自源XML命名空间,而是HTML命名空间,并且应该以此方式处理,或者根本不作为标签,因此作为CDATA的一部分。 - Dennis Kreminsky
1
他的XML中没有任何命名空间引用的迹象,而且他还试图输出HTML。他的输入没有包含CDATA,所以如果有的话,告诉他如何处理也没什么意义。 - James Walford
重点是,他的代码可以包含任何HTML标记,包括格式不正确的文本等,它不应该像处理源XML一样处理。请理解我的想法,这不是关于正式命名空间,而是关于<favoriteMovies>作为结构和<i>作为标记的逻辑使用范围。 - Dennis Kreminsky
3
就我所看到的,你的评论与原始XML的构建更相关。如果它包含未在CDATA中的未关闭HTML元素,则无论如何都不会是格式良好的。如果他的XML格式良好,但仅仅包含一些没有显式命名空间的HTML元素,那么仅仅复制它们并不是一个问题。 - James Walford

2

你应该使用'disable-output-escaping'属性。元素的一般格式如下:

<xsl:value-of select="expression" disable-output-escaping="yes|no" />

'disable-output-escaping'是可选的。"yes"表示特殊字符(如"<")应按原样输出。"no"表示特殊字符(如"<")应输出为"<"。默认值为"no"。

因此,只需将您的代码更改为:

<xsl:template match="favoriteMovie">
  <xsl:copy-of select="node()" disable-output-escaping="yes"/>
</xsl:template>

为什么我会收到“'disable-output-escaping' 是 'xsl:copy-of' 元素的无效属性”这个错误信息? - FMFF
disable-output-escaping 并不是普遍支持的,使用它是不被鼓励的。请参见 https://dev59.com/b3RB5IYBdhLWcg3wJklC#701793。 - chiborg

-7

有两件事需要注意。

首先,确保在CDATA中筛选标签。

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="test.html.xsl"?>
<favoriteMovies>
    <favoriteMovie><![CDATA[the <i>Star Wars</i> saga]]></favoriteMovie>
</favoriteMovies>

第二步。禁用输出转义:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" />
<xsl:template match="/">
  <html>
    <head />
      <body>
        <ul>
            <xsl:for-each select="favoriteMovies/favoriteMovie">
                <li><xsl:value-of select="." disable-output-escaping="yes" /></li>
            </xsl:for-each>
        </ul>
      </body>
  </html>
</xsl:template>

编辑:使用编辑器成功解决了问题,现在代码显示正常。

编辑2:在您的代码中包含了更改。

编辑3:关于问题的领域是有关电影信息的结构化,而不是HTML数据。 HTML仅用于标记目的,想象一下,在favoriteMovie中有html标题标签,而同名的标签标题可以是数据库中的有效标签。这些标题显然必须以不同的方式进行解释。这就证明了在处理时使用CDATA,然后禁用输出的合理性。


无法按预期工作;HTML 最终看起来像这样:<i>Star Wars</i> 传奇。 - Craig W
使用gt和lt实体是我为了对抗编辑器问题而尝试的,不要在您的文档中使用它们。关键是要使用CDATA将HTML标记包装在XML标记内部。 - Dennis Kreminsky
似乎没有改变任何东西,<i>标签仍然被忽略。 - Craig W
啊,抱歉,一直以为你给了我两个不同的选项来完成它!非常好用。实际上我正在同时从XML转换成HTML和一个简单的文本文件进行XSLT转换--我不知道有没有什么简单的方法可以防止<i>标签出现在文本文件中? - Craig W
1
回复:编辑3 - 你似乎在试图回答他没有问的问题。 - James Walford
显示剩余4条评论

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