强制HTML Tidy输出XML(而不是XHTML),或强制XSLTproc解析XHTML文件。

3
我有许多HTML文件需要使用XSLT处理,并使用一个XML文件选择哪些HTML文件以及对它们进行什么操作。
我尝试过:
1. 使用HTML Tidy将HTML转换为XHTML / XML 2. 在XSLT中使用document(filename)读取特定的XHTML / XML文件 3. ...使用标准的nodeset命令访问“html / body / *”等
但这不起作用,因为:
1. XSLT(尝试过:libXSLT / xsltproc ...和Saxon)似乎无法处理XHTML文档作为外部文件(它看到xhtml DOCTYPE并拒绝将其解析为节点)。
好吧(我想)... XHTML只是XML,我只需要通过HTML Tidy处理它,然后说:
"output-xml yes ... output-html no ... output-xhtml no"
但如果你尝试这样做,HTML Tidy会忽略你,并强制使用html。它似乎硬编码为仅在输入最初为XML时才输出XML文件。
有没有办法:
1. 强制HTML Tidy遵守命令行参数并设置我要求的doctype 2. 强制XSLTproc将xhtml DOCTYPE解析为xml 3. ...其他聪明的方法?
注意:这必须在OS X上工作-它是iOS应用程序构建过程的一部分。这不应该是一个大问题,但例如任何仅限于Windows的工具不可用。我想使用标准的开源跨平台工具(如tidy、libxslt等)来实现这一目标。
5个回答

2
我最终发现为什么XSLTproc / Saxon拒绝解析文件,如果它们被传入一个DOCTYPE html:外部文档的DOCTYPE改变了它们解释xmlns(命名空间)指令的方式。Tidy正确地声明了“xmlns=...xhtml: 命名空间”,因此在我的XSLT中所有节点名称都是不存在的,XSLT忽略它们,好像它们不存在一样——它需要我提供一个兼容映射到相同命名空间的解决方案。
奇怪的是,如果DOCTYPE是xml,那么它们会愉快地忽略xmlns命令,或者允许我通过未限定名称引用节点。这让我误以为它们在xhtml DOCTYPE版本中根本忽略了节点集。
因此,“解决方案”类似于以下内容:
1.修改你的XSLT样式表,也导入“xhtml”命名空间——请注意:这是必需的,以便您可以引用外部文件中的节点。 2.编写所有的XSL匹配/选择/模板规则,在每个节点上都加上“xhtml”前缀(以及每个属性,我想?) 3.让Tidy输出任何它想要的东西:它不重要,一旦你在里面有命名空间支持,它就会正常工作。
示例代码:
  1. Your stylesheet goes from this:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    

    ...to this:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
    
  2. Your select / match / document-import goes from this:

    <xsl:copy-of select="document('html-files/file1.htm')/html/body"/>
    

    ...to this:

    <xsl:copy-of select="document('html-files/file1.htm')/xhtml:html/xhtml:body"/>
    

注意:为了明确,如果忽略命名空间,那么似乎XSLT可以处理未包含DOCTYPE声明的文件,即使其中有命名空间。不要犯我犯过的错误,认为你的XSLT是正确的,仅仅因为它看起来是正确的 :)


0

XHTMLXML(如果它是有效的)。

要将您的XHTML作为XML处理,您不能将其作为"text/html" MIME提供。请改用application/xhtml+xml(请记住,IE6不支持呈现此内容,并且会提示下载窗口来访问您的站点)。

在PHP中,可以使用header()函数将其提供为xhtml+xml。

我认为这应该能解决问题:

header('Content-Type: application/xhtml+xml');

这个有帮助吗?


如果一开始你就在使用PHP,那么你只需要将(X)HTML作为DOMDocument加载,同样的方法用于XSLT样式表,并使用PHP类直接处理转换,而不是通过xsltproc。 - user268396
这不是关于Web服务器的问题,而是关于文档类型的问题。在整个过程中没有涉及到Web服务器。如果您不了解DOCTYPE以及它如何影响XSLT处理,请先去了解一下。DOCTYPE是文档本身的一部分,而MIME类型则不是。 - Adam
问题在于XHTML文档可能会作为XML文档进行验证,但根据XSLT,它们并不是同一种东西。特别是,在XSLT中,document()函数的定义取决于它找到的两个文档类型之一而有所不同。我找不到一种方法来强制document()将所有内容都视为纯XML——如果您知道如何做到这一点,我认为这将解决问题? - Adam

0

虽然已经有一段时间了,但我还记得曾经尝试使用HTMLTidy来准备HTML文件以供XSLT使用,但很失望地发现它在尝试“格式化”HTML时很容易放弃。后来我找到了TagSoup,并感到非常满意。

TagSoup还包括一个命令行处理器,可以读取HTML文件并生成干净的HTML或接近XHTML的格式良好的XML。

我不知道您是否绑定了HTMLTidy,但如果没有,请尝试这个:http://home.ccil.org/~cowan/tagsoup/

作为示例,这是一个糟糕的HTML文件:

<body>
  <p>Testing
</body>

这里是Tagsoup命令及其输出:

~ zyoung$ java -jar /usr/local/tagsoup-1.2.jar --html bad.html 
src: bad.html
<html><body>
  <p>Testing
</p></body></html>

编辑 01

这是 tagsoup 如何处理 DOCTYPE 的方式。

这是一个有有效 DOCTYPE 的糟糕 HTML 文件:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
  <p>Testing
</body>
</html>

这是tagsoup的处理方法:

~ zyoung$ java -jar /usr/local/tagsoup-1.2.jar --html bad.html 
src: bad.html
<html><body>
  <p>Testing
</p></body></html>

只有在明确向tagsoup传递DOCTYPE时,它才会尝试输出一个。
~ zyoung$ java -jar /usr/local/tagsoup-1.2.jar --html --doctype-public=html bad.html 
src: bad.html
<!DOCTYPE  PUBLIC "html" "">
<html><body>
  <p>Testing
</p></body></html>

希望这可以帮到你,
扎卡里


看起来我已经成功地将Tidy和XSLTproc搭配使用了(请参见下面的答案)- 但是TagSoup也看起来是一个不错的工具。下次如果Tidy出了问题,我肯定会尝试一下TagSoup。然而,根据文档,你无法更改Doctype - 只能更改其Public和System元素(位于末尾的两个引号字符串)吗? - Adam
@Adam:我将成为第一个承认仍然让我感到神秘的DOCTYPE,而且只有在必要时才会“处理”它们。话虽如此,我可能会错过与你的问题有关的某些内容;但我认为上面展示了如何处理你问题中的DOCTYPE方面。未指定的命名空间是另一件有时会令我困惑的事情。 - Zach Young
DOCTYPE应该是:“!DOCTYPE html PUBLIC“something big goes here”“something big goes here””...即引号中的内容不是用于建立基本doctype的。不是吗? - Adam

0
如果你运行xsltproc --help,在接受的输入标志中有一个非常显眼的叫做--html,它据说告诉xsltproc

--html:输入文档是HTML文件

想必为了使其正常工作,你必须首先拥有有效的HTML文件。因此,你可能需要先整理一下它们。


谢谢,我试过了,但那个标志似乎没什么用 - 它会导致所有输入文件都被读取为HTML,即使主输入文件是XML(因此它会导致XSLTproc立即崩溃,因为每个XML标记都是无效的HTML :)) - 实际上我们需要有选择性地指定文件是HTML,而不是XML。 - Adam

0

我认为主要问题是由XML目录文档类型声明引起的。您可以通过删除输入XHTML中的外部实体引用并查看处理器是否正确处理它来测试此功能。

我会按照以下步骤进行:

主要问题在于Saxon和xsltproc没有任何选项可以禁用外部实体解析。这由MSXSL.exe命令行实用程序支持,选项为-xe


是的 - '-xe'选项听起来很有用。我尝试了省略doctype,正如你所说,它导致所有外部实体都出现了严重问题。但是,你怎么在XSLT端“添加doctype”呢?我一直在想:“如果document()有一个额外的参数,让我覆盖传入的DOCTYPE,那就可以解决了”,但它没有 :(。 - Adam
我添加了一个链接,描述了如何在XSLT侧处理文档类型。现在请检查我的回答。 - Emiliano Poggi
所以...如果我理解正确:通过在XSLT中添加文档类型,并以某种方式将所有实体映射放入XSLT中(手动编写它们?),那么您就能够跳过外部文档中充满意外实体的问题了? - Adam
并不是。我只是建议在XSLT方面添加DTD引用,如下所示(http://www.dpawson.co.uk/xsl/sect2/N2281.html#d3805e19),确保(使用-omit选项)不在输入文档中包含将被处理器解析的DTD引用。 - Emiliano Poggi

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