我需要对一些结构不好的HTML进行后期处理 - 例如
<html>
<body>...</body>
<body>...</body>
</html>
转换此HTML的最佳方法是什么,以便第二个正文的内容出现在第一个正文中,当然除了额外的正文标记?我不想用这个规则操纵任何其他东西。
我想过在html标签上进行匹配并使用显式应用模板调用从那里进行处理,但对我来说似乎有点草率。我知道如何匹配虚假的身体(“body [position()&gt; 1]”)但我想了解如何最好地编写变换。
编辑:我确实需要将其他模板应用于所有这些元素的子元素,因此简单的副本将无效。
我想保留评论和处理说明。我希望整个文档几乎都是身份转换,除了这些多个实体和其他一些小编辑,我已经成功完成了。
编辑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>
答案 0 :(得分:2)
将这些模板添加到身份转换中:
<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[2]
你可以使用body[position() != 1]
。这将处理您的输入具有两个以上body
元素的情况。
答案 1 :(得分:1)
通常通过编写量身定制的黑客来避免下游问题 导致代码库管理不善。
你应该最好在它的来源修复损坏的HTML, 有几个身体标签听起来像某个地方的严重误解。
答案 2 :(得分:1)
如果您的输入HTML是格式良好的XML,那么此XSLT模板将执行此操作:
<xsl:template match="/">
<body>
<xsl:copy-of select="//body/node()" />
</body>
</xsl:template>
(我在这个例子中并不关心<html>
节点,因为这很简单。)
上述更灵活的变体(根据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>
答案 3 :(得分:1)
也许这更接近你所追求的目标:
<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>
这使用流行的“推送”结构作为模板,因此您可能会发现它更灵活。
答案 4 :(得分:1)
我认为@Keltex意味着你应该剥离
</body>\s*<body>
在处理文档之前,以便您可以像编写标准化输入一样编写XSLT。
这就是我要做的事。
(这假设多个身体标签之间没有内容。)
编辑:这不会删除正文标记的内容。请注意,您要将结束正文标记中的任何内容删除到开头。这将留下初始和最终标签。换句话说,输入就像这样
<body>
good stuff
</body>
<body>
more good stuff
</body>
你会在中间瞄准这两个标签。删除这些将产生一个连续的身体:
<body>
good stuff
more good stuff
</body>
答案 5 :(得分:-1)
如果HTML搞砸了,那么我不愿意假设HTML已经很好地形成了使用xlst。您可能只想使用正则表达式来查找
<body>(whitespace)</body>
并将其删除。