如何将HTML转换为有效的XHTML?

9

我有一个HTML字符串,在这个例子中它看起来像:

<img src="somepic.jpg" someAtrib="1" >

我正在尝试编写一个正则表达式,以匹配“img”节点并在节点末尾应用斜杠,使其看起来像这样。

<img src="somepic.jpg" someAtrib="1" />

本质上,这里的最终目标是确保节点被关闭,虽然在HTML中开放节点是有效的,但在XML中却不是。有没有正则表达式专家可以提供帮助?


3
不应使用正则表达式解析(X)HTML。HTML不是规则的,因此不适合于正则表达式。请使用完整的HTML解析器。 - David B
1
@DavidB 我明白你的意思。但是我正在尝试操作一个“字符串”,这就是为什么我在问这个问题 :) - John
原始标签无效,请求的 XHTML 标签也无效。你是真的想说“格式良好”而不是“有效”吗? - Jukka K. Korpela
4个回答

18

不要使用正则表达式,而是使用专用的解析器。在JavaScript中,使用DOMParser创建文档,然后使用XMLSerializer对其进行序列化:

var doc = new DOMParser().parseFromString('<img src="foo">', 'text/html');
var result = new XMLSerializer().serializeToString(doc);
// result:
// <html xmlns="http://www.w3.org/1999/xhtml"><head></head><body> (no line break)
// <img src="foo" /></body></html>

如果你需要在Node.js后端使用这个,你必须使用xmldom。运行npm i xmldom即可安装。


嘿,Rob。我想学习如何使用这个技术。我正在运行Opera9、IE6和FF2(为了向后兼容性,我的所有代码都必须在旧版本浏览器上运行),但上述代码不能直接工作。还需要包含什么其他内容才能使其正常工作?你能发一个完整可用的函数吗?谢谢。 - ridgerunner
1
@ridgerunner,它支持IE9+,FF 12+(4+需要DOMParser text/html polyfill), Opera 11.6+(使用DOMParser polyfill)。Chrome(21)存在一个错误,即未添加“/>”。抱歉我没有详细说明,我现在很忙。如果你愿意,可以编辑我的/你的答案使它更完整。 - Rob W
仍然无法在Chrome(34)中工作,因此这是一种很酷的技术,但如果您的用户正在使用Chrome,则不适用。 - rossdavidh

4

您可以创建一个xhtml文档并导入/采用html元素。 当然,可以通过HTMLElement.innerHTML属性解析HTML字符串。 关键点是使用Document.importNode()或Document.adoptNode()方法将HTML节点转换为XHTML节点:

var di = document.implementation;
var hd = di.createHTMLDocument();
var xd = di.createDocument('http://www.w3.org/1999/xhtml', 'html', null);
hd.body.innerHTML = '<img>';
var img = hd.body.firstElementChild;
var xb = xd.createElement('body');
xd.documentElement.appendChild(xb);
console.log('html doc:\n' + hd.documentElement.outerHTML + '\n');
console.log('xhtml doc:\n' + xd.documentElement.outerHTML + '\n');
img = xd.importNode(img); //or xd.adoptNode(img). Now img is an xhtml element
xb.appendChild(img);
console.log('xhtml doc after import/adopt img from html:\n' + xd.documentElement.outerHTML + '\n');

输出结果应该是:
html doc:
<html><head></head><body><img></body></html>

xhtml doc:
<html xmlns="http://www.w3.org/1999/xhtml"><body></body></html>

xhtml doc after import/adopt img from html:
<html xmlns="http://www.w3.org/1999/xhtml"><body><img /></body></html>

Rob W 的解答在谷歌浏览器中(至少29版及以下)无法使用,因为 DOMParser 不支持“text/html”类型,而 XMLSerializer 在谷歌浏览器中为 HTML 文档生成 HTML 语法(不是 XHTML)。


这似乎是比Rob W的更好的解决方案。importNode()有第二个参数,如果您还想转换后代元素。 - fishbone

2
除了Rob W的答案,你还可以使用正则表达式提取正文内容:
var doc = new DOMParser().parseFromString('<img src="foo">', 'text/html');
var result = new XMLSerializer().serializeToString(doc);

/<body>(.*)<\/body>/im.exec(result);
result = RegExp.$1;

// result:
// <img src="foo" />

注意:在IE9中,parseFromString(htmlString, 'text/html');会抛出错误,因为text/html MIME类型不被IE9支持。但是在IE10和IE11中可以使用。


为什么要使用正则表达式?你可以简单地使用doc.body.innerHTML。 - Krunoslav Djakovic
更正一下。innerHTML会返回<br>而不是<br/>。但是这个正则表达式模式效果更好https://dev59.com/flDTa4cB1Zd3GeqPHTY3 - Krunoslav Djakovic

1

这将会做得相当不错:

result = text.replace(/(<img\b[^<>]*[^<>\/])>/ig, "$1 />");

补充说明:如果您的代码包含带有尖括号的标签属性(这不是有效的XML / XHTML),那么这个会做得更好一些:
result = text.replace(/(<img\b(?:[^<>"'\/]+|'[^']*'|"[^"]*")*)>/ig, "$1 />");

@John 正则表达式不能用于创建符合XHTML标准的文档的原因是它不可靠。例如,这个答案在 <img alt="2 > 1" src="2gt1"> 处已经失败了。输出结果为 <img alt="2 /> 1" src="2gt1"> - Rob W

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