如何在没有网络访问的情况下导入XML架构?

时间:2014-03-12 15:28:58

标签: android ios xml xsd libxml2

我使用 libxml2 实现架构验证。模式我正在使用以下行来验证导入其他两个模式:

<xs:import namespace="http://www.w3.org/XML/1998/namespace"
           schemaLocation="http://www.somewebsite.com/xsd/xml.xsd"/>

所有三个架构文件都位于设备的同一目录中。

当设备具有互联网访问权限时,这种方法很有效,但是当设备没有访问时会失败,因为即使我传入schemaLocation,libxml2仍会尝试从XML_PARSE_NONET下载导入的模式。

我尝试通过将schemaLocation属性编辑为xml.xsd./xml.xsdfile:///data/data/com.company.appname/files/xml.xsd来让libxml2在本地加载文件,但这三个都导致了相同的libxml2错误:

  • 域名:16
  • 代码:3069 (XML_SCHEMAP_INTERNAL)
  • 消息:Internal error: xmlSchemaParse, An internal error occurred.

我还试图完全删除schemaLocation属性,因为libxml2可能会在原始模式旁边搜索导入的模式,但是当模式解析器触及引用的行时,会导致以下错误导入的实体:

<xs:attribute ref="xml:lang" use="required"/>
  • 域名:16
  • 代码:3004 (XML_SCHEMAP_SRC_RESOLVE)
  • 消息:attribute use (unknown), attribute 'ref': The QName value '{http://www.w3.org/XML/1998/namespace}lang' does not resolve to a(n) attribute declaration.

我还考虑将三个模式手动合并到一个文件中,但由于它们使用不同的名称空间,因此无法实现。

标准解决方案似乎是 XML目录,但我已经通读了libxml2's catalog documentation,我无法弄清楚如何(甚至是否可以)添加部署到设备时我的应用程序使用的映射。我想我可能需要实现一个xmlExternalEntityLoader,但是文档很简单。

如何在没有网络访问权限的情况下让libxml2导入这些模式?显然,我理想地喜欢一个与未经编辑的模式一起工作的强大解决方案,但我对包含编辑模式的快速和肮脏的内容感到满意,就像我上面描述的原始尝试一样。

上述错误来自Android设备(使用JNI),但我在iOS上遇到类似问题,解决方案也需要工作。

1 个答案:

答案 0 :(得分:0)

执行此操作的一种方法是拦截libxml2的调用,以使用自定义xmlExternalEntityLoader打开导入的URL。

执行此操作的基本代码如下:

#include <libxml/xmlIO.h>
#include <libxml/parserinternals.h>

xmlExternalEntityLoader defaultLoader = NULL;

xmlParserInputPtr
xmlMyExternalEntityLoader(const char *URL, const char *ID,
                          xmlParserCtxtPtr ctxt) {
    xmlParserInputPtr ret;
    const char *fileID = NULL;
    /* lookup for the fileID
     * The documentation suggests using the ID, but for me this was
     * always NULL so I had to lookup by URL instead.
     */

    ret = xmlNewInputFromFile(ctxt, fileID);
    if (ret != NULL)
        return(ret);
    if (defaultLoader != NULL)
        ret = defaultLoader(URL, ID, ctxt);
    return(ret);
}

int main(..) {
    ...

    /*
     * Install our own entity loader
     */
    defaultLoader = xmlGetExternalEntityLoader();
    xmlSetExternalEntityLoader(xmlMyExternalEntityLoader);

    ...
}

(稍微调整了libxml2的I / O接口文档的The entities loader部分中的示例代码。)