libxml2:xmlIOParseDTD:I/O 警告:加载 HTTP 资源失败

libxml2: xmlIOParseDTD: I/O warning : failed to load HTTP resource

我似乎无法让 libxml2 从内存中正确解析 DTD:DTD 包含对指向 w3c.org 的外部 XHTML 实体的引用。链接有效,浏览器从中加载内容就好了。但是,libxml2 报告加载 HTTP 资源失败,即使从 xmlIOParseDTD 函数返回成功状态也是如此。

这是重现问题的最小测试:

#include "libxml/xmlreader.h"
#include <string>
#include <fstream>
#include <iostream>

int main()
{
    // Read DTD from file
    std::ifstream f;
    f.open("enml2.dtd");
    if (!f.is_open()) {
        std::cerr << "Can't open enml2.dtd file" << std::endl;
        return 1;
    }

    std::string enml;
    std::string line;
    while(getline(f, line))
    {
        enml += line;
    }

    f.close();

    // Init parser options
    xmlInitParser();
    xmlSubstituteEntitiesDefault(1);
    xmlLoadExtDtdDefaultValue = 1;

    // Parse DTD from memory
    xmlParserInputBufferPtr pBuf = xmlParserInputBufferCreateMem(enml.c_str(), enml.size(),
                                                             XML_CHAR_ENCODING_UTF8);
    if (!pBuf) {
        std::cerr << "can't allocate input buffer for dtd validation" << std::endl;
        return 2;
    }

    xmlDtdPtr pDtd = xmlIOParseDTD(NULL, pBuf, XML_CHAR_ENCODING_UTF8);
    if (!pDtd) {
        std::cerr << "can't parse dtd from buffer" << std::endl;
        return 3;
    }

    std::cout << "Successfully parsed DTD" << std::endl;
    xmlFreeDtd(pDtd);
    return 0;
}

提到的enml2.dtd文件可以从这里下载:http://xml.evernote.com/pub/enml2.dtd

构建(在我的例子中是 Linux):

g++ -I/usr/include/libxml2 main.cpp -o libxml2-test -lxml2

运行:

./libxml2-test 
I/O warning : failed to load HTTP resource
n 1 for XHTML//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">%HTMLlat1;
                                                                               ^
 %HTMLlat1; 
           ^
I/O warning : failed to load HTTP resource
for XHTML//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent">%HTMLsymbol;
                                                                               ^
 %HTMLsymbol; 
         ^
I/O warning : failed to load HTTP resource
for XHTML//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent">%HTMLspecial;
                                                                               ^
 %HTMLspecial; 
              ^
Successfully parsed DTD

libxml2使用的版本是2.9.1+dfsg1-3ubuntu4.4,我在LinuxMint 17(对应Ubuntu14.04)。

Upd.: 我在 OS X 10.9 上观察到与 libxml2 2.9.0 相同的事情。此外,xmllint 命令行实用程序无法以与我的示例代码完全相同的方式获取这些外部条目,即使我使用 --loaddtd 选项明确允许获取外部 DTD。要么我真的遗漏了一些关于它应该如何工作的东西,要么我遇到了 libxml2 的错误。

看来问题不在 libxml2 中,而是在 w3c 站点中,外部实体在相关 dtd 文件中使用了对它的引用。可以在 the answer to the similar question 中找到更多详细信息。我通过使用浏览器从链接下载 .ent 文件并将其全部内容包含到 dtd 文件而不是引用中来解决这个问题。