【问题标题】:How to parse and modify XHTML in Node.js (supporting HTML entities and CDATA sections)?如何在 Node.js 中解析和修改 XHTML(支持 HTML 实体和 CDATA 部分)?
【发布时间】:2021-12-12 05:54:44
【问题描述】:

我正在开发一个接收 XHTML sn-p(Confluence 存储格式)的 Node.js 应用程序,应该对其进行一些修改,然后将修改后的 XHTML 发回。 XHTML 可能包含 HTML 实体(例如 ö)以及 CDATA 部分(例如 <![CDATA[test]]>)。

我遇到的挑战是使用我尝试过的解析器,当我在 HTML 模式下解析 sn-p 时,CDATA 部分会中断,但是当我在 XML 模式下解析它时,HTML 实体是没有正确解释。

以下是我如何让它在浏览器中工作的示例,但我如何使用 jsdom 和 Cheerio 无法让它工作。是否有任何其他库可以用来实现这一点,或者有任何不同的方式来使用 jsdom 或 Cheerio?

在浏览器中

在浏览器中,我可以在 XML 模式下使用DOMParser。使用测试 sn-p <span>ö<![CDATA[ä]]></span>,我可以将它包装在 XHTML 正文中:

const doc = new DOMParser().parseFromString(`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><body><span>&ouml;<![CDATA[ä]]></span></body></html>`, 'application/xml');
doc.querySelector('body').innerHTML;   // <span>ö<![CDATA[ä]]></span>
doc.querySelector('body').textContent; // öä

XML MIME 类型确保正确解释 CDATA 部分,而 XHTML DOCTYPE 确保支持实体。

jsdom

为了在 Node.js 中实现同样的效果,我尝试使用 jsdom。问题是当我在 HTML 模式下解析代码时,CDATA 部分被转换为注释,但是当我在 XML 模式下解析它时,由于 HTML 实体而引发异常:

import { JSDOM } from 'jsdom';
const xhtml = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><body><span>&ouml;<![CDATA[ä]]></span></body></html>`;

new JSDOM(xhtml).window.document.body.innerHTML; // <span>ö<!--[CDATA[ä]]--></span>
new JSDOM(xhtml).window.document.body.textContent; // ö
new JSDOM(xhtml, { contentType: 'application/xml' }); // Uncaught DOMException [SyntaxError]: about:blank:1:186: undefined entity.

更新:我有reported jsdom 的问题。

欢呼

我在后端进行 DOM 修改的首选方法是cheerio。在 HTML 模式下使用cheerio,CDATA 部分被转换为注释。在 XML 模式中,实体不会被解释,而是被双重转义为 &amp;amp;ouml;。在不解码实体的 XML 模式下,XHTML 被正确保留,但实体没有被正确解释,在获取文本内容时可以看到。

import cheerio from 'cheerio';
const xhtml = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><body><span>&ouml;<![CDATA[ä]]></span></body></html>`;

cheerio.load(xhtml).root().find('body').html(); // <span>ö<!--[CDATA[ä]]--></span>
cheerio.load(xhtml).root().find('body').text(); // ö
cheerio.load(xhtml, { xmlMode: true }).root().find('body').html(); // <span>&amp;ouml;<![CDATA[ä]]></span>
cheerio.load(xhtml, { xmlMode: true }).root().find('body').html(); // &ouml;ä
cheerio.load(xhtml, { xmlMode: true, decodeEntities: false }).root().find('body').html(); // <span>&ouml;<![CDATA[ä]]></span>
cheerio.load(xhtml, { xmlMode: true, decodeEntities: false }).root().find('body').text(); // &ouml;ä

更新:我有reported 的问题要给cheerio。

【问题讨论】:

  • 你试过xmldom吗?它应该为 node.js 提供 DOMParser 接口和功能。请注意,您应该设置mimeType application/xhtml+xml
  • @bigless 它还显示“未找到实体”错误。

标签: javascript node.js dom xhtml


【解决方案1】:

我是 pointed out 解决cheerio 问题的方法:

cheerio.load(xhtml, { xml: { xmlMode: false, recognizeCDATA: true, recognizeSelfClosing: true } });

使用这些选项,我可以在 Node.js 环境中成功解析 XHTML。

除了这个解决方案之外,我注意到在浏览器中使用DOMParser 的缺点是浏览器之间存在不一致。特别是,在将查询选择器与 XML 命名空间结合使用时,我有时必须在查询中包含命名空间,有时则不需要。由于这些不一致,jquery 也officially doesn't support XML namespaces。为了在浏览器之间以及前端、前端测试和后端之间实现一致的行为,我决定使用cheerio,甚至在浏览器中解析XHTML。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-23
    • 2014-01-04
    • 2015-05-22
    • 2017-08-05
    • 1970-01-01
    • 2014-01-13
    相关资源
    最近更新 更多