【问题标题】:XSLT: help me fix multiple BODY tagsXSLT:帮我修复多个 BODY 标签
【发布时间】:2010-10-14 23:31:50
【问题描述】:

我需要对一些结构不好的 HTML 进行后处理——例如

<html>
<body>...</body>
<body>...</body>
</html>

转换此 HTML 以使第二个正文的内容出现在第一个正文中的最佳方法是什么,当然除了额外的正文标记之外?我不想用这条规则来操纵其他任何东西。

我曾想过匹配 html 标记并使用显式的 apply-templates 调用从那里处理它,但对我来说似乎有点草率。我知道如何匹配虚假主体(“body[position() > 1]”),但我想了解如何最好地编写转换。

编辑:我确实需要将其他模板应用于所有这些元素的子元素,因此简单的副本将不起作用。

我想保留 cmets 和处理指令。我几乎希望将整个文档作为身份转换,除了这些多个主体和一些其他小的编辑,我已经成功地做到了。

编辑 2:在上面的示例中,保留第二个 body 元素的子元素很重要。它们应该是输出中第一个 body 标记的子节点,位于第一个 body 标记的子节点的末尾。

编辑 3:这是一些说明性的输入/输出(未检查有效性):

<html>
  <!-- Look at my comments -->
  <head>
    <title>My title!</title>
    <!-- Commentary -->
  </head>
  <body>
     <p>Something <b>bold</b></p>
  </body>
  <body>
     <!-- heh -->
     <p>Some bozo put my parent in here.</p>
  </body>
  <body>
     <p>More stuff here</p>
  </body>
</html>

需要:

<html>
  <!-- Look at my comments -->
  <head>
    <title>My title!</title>
    <!-- Commentary -->
  </head>
  <body>
     <p>Something <b>bold</b></p>
     <!-- heh -->
     <p>Some bozo put my parent in here.</p>
     <p>More stuff here</p>
  </body>
</html>

【问题讨论】:

    标签: html xslt


    【解决方案1】:

    通常通过编写量身定制的 hack 来避免下游问题 导致代码库难以管理。

    您最好在源代码处修复损坏的 HTML, 有几个身体标签在某处听起来像是一个严重的误解。

    【讨论】:

    • 不幸的是,在这种情况下这是不可能的。
    【解决方案2】:

    如果您输入的 HTML 是格式正确的 XML,那么这个 XSLT 模板可以做到:

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

    (在此示例中,我不关心 &lt;html&gt; 节点,因为这很简单。)

    上述更灵活的变体(根据 OP 的要求)

    <!-- explicitly catching the initial html circumvents built-in templates -->
    <xsl:template match="/html">
      <xsl:copy>
        <xsl:apply-templates />
      </xsl:copy>
    </xsl:template>
    
    <!-- copy everything that is not processed otherwise -->
    <xsl:template match="@*|node()|processing-instruction()">
      <xsl:copy-of select="." />
    </xsl:template>
    
    <!-- matches any "body" node, but produces output only for the first -->
    <xsl:template match="body">
      <xsl:if test="not(preceding-sibling::body)">
        <xsl:copy>
          <xsl:apply-templates select="//body/@*|//body/node()" />
        </xsl:copy>
      </xsl:if>
    </xsl:template>
    
    <!-- you can add more of these specific templates, as needed -->
    <xsl:template match="body//a">
      <b>
        <xsl:copy-of select="." />
      </b>
    </xsl:template>
    

    这个输入:

    <html>
      <head><title>Foo!</title></head>
      <?dummy processing instruction?>
      <body foo="bar">...<a href="foo">asd</a><!-- comment --></body>
      <body>...contents of body#2...</body>
    </html>
    

    得到这个结果(空格和缩进为了可读性而改变):

    <html>
      <head><title>Foo!</title></head>
      <?dummy processing instruction?>
      <body foo="bar">
        ...
        <b><a href="foo">asd</a></b>
        <!-- comment -->
        ...contents of body#2...
      </body>
    </html>
    

    【讨论】:

    • 我应该告诉你我确实需要将其他模板应用于 body 的子级。
    • 好吧,那就用吧。
    • 还有head、cmets和处理指令。问题是在父 HTML 上下文中做很多事情似乎让我明确命名/应用我不想命名/应用的东西,因此“混乱”。
    【解决方案3】:

    如果 HTML 是乱七八糟的,那么我不愿意假设 HTML 的格式足够好,可以使用 xlst。您可能只想使用正则表达式来查找

    <body>(whitespace)</body>
    

    并删除它。

    【讨论】:

    • HTML 正在由 TagSoup 处理,因此是格式良好的 XML。
    【解决方案4】:

    也许这更接近你所追求的:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0" exclude-result-prefixes="xsl">
    <xsl:output indent="yes" method="html"/>
    
    <xsl:template match="/">
        <xsl:apply-templates select="@*|node()"/>
    </xsl:template>
    
    <!-- Identity Template -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- Matches on the first 'body' tag -->
    <xsl:template match="body[1]">
        <xsl:copy>
            <!-- apply=templates the children of all the body tags -->
            <xsl:apply-templates select="//body/node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- Skip processing on the subsequent body tags 
         (their children are still processed however)   -->
    <xsl:template match="body"/>
    
    </xsl:stylesheet>
    

    这对模板使用了流行的“推送”结构,因此您可能会发现它更灵活。

    【讨论】:

    • 这与我所拥有的很接近,但我想保留其他body标签的孩子,只需将它们合并到第一个标签的末尾即可。我相信这个样式表会删除它们。
    • 请注意,使用“//body”会查找文档中的所有“body”元素,而不仅仅是“html”元素的子元素。
    • @Steven 虽然看起来就是这样做的,但请仔细看看。由于我们正在应用模板,它确实会复制每个正文元素的内容。
    • @Robert 感谢您指出这一点。更具体地说,XPath 可以是 '/html/body/node()'
    【解决方案5】:

    我认为@Keltex 的意思是你应该去掉

    </body>\s*<body>
    

    在处理文档之前,您可以像编写规范化输入一样编写 XSLT。

    这就是我会做的。

    (假设多个body标签之间没有内容。)

    编辑: 这不会删除正文标签的内容。请注意,您将从 closure 正文标记中删除任何内容到 opening 标记。这将保留初始和最终标签。换句话说,像这样的输入

    <body>
        good stuff
    </body>
    <body>
        more good stuff
    </body>
    

    您将定位中间的这两个标签。移除这些将产生一个单一的、连续的主体:

    <body>
        good stuff
        more good stuff
    </body>
    

    【讨论】:

    • 但我想要那些额外的正文标签的内容。它们出现在浏览器中。
    • 是的,我就是这么说的。
    【解决方案6】:

    将这些模板添加到身份转换中:

    <xsl:template match="/html/body[1]">
       <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
          <xsl:apply-templates select="/html/body[2]/node() | /html/body[2]/@*"/>
       </xsl:copy>
    </xsl:template>
    
    <xsl:template match="/html/body"/>
    

    编辑:

    要对此束手无策,您可以使用body[position() != 1],而不是上面的body[2]。这将处理您的输入包含两个以上 body 元素的情况。

    【讨论】:

    • 腰带和吊带很好,因为至少有三个body标签:)
    • 夫人。罗斯尼没有抚养任何愚蠢的孩子。其实这不是真的,但不是我。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 2011-03-07
    • 2023-03-21
    • 2017-06-24
    • 2014-02-22
    • 2013-09-09
    相关资源
    最近更新 更多