使用带有nodejs的libxmljs的无效XSD架构

时间:2014-10-10 07:48:55

标签: node.js xsd-validation xliff libxml-js

我在libxmljs存储库上发布了一个问题,因为他们认为这不是lib级别的问题所以它已经关闭了。所以我在这里发布。

我尝试使用OASIS提供的XML架构验证XLIFF文件,但我一直收到XSD错误。

  

错误:XSD架构无效      在Document.validate(/Users/fluxb0x/Tests/xliff_parser/node_modules/libxmljs/lib/document.js:73:17)      在Request._callback(/Users/fluxb0x/Tests/xliff_parser/main.js:25:21)      在Request.self.callback(/Users/fluxb0x/Tests/xliff_parser/node_modules/request/request.js:199:22)      在Request.emit(events.js:98:17)      在请求。 (/Users/fluxb0x/Tests/xliff_parser/node_modules/request/request.js:1160:14)      在Request.emit(events.js:117:20)      在IncomingMessage。 (/Users/fluxb0x/Tests/xliff_parser/node_modules/request/request.js:1111:12)      在IncomingMessage.emit(events.js:117:20)      at _stream_readable.js:938:16      at process._tickCallback(node.js:419:13)

我已经使用Oxygen XML编辑器来测试验证,它没有问题。

这是我导出的XLIFF文件:en.xliff

这是OASIS提供的XSD文件:xliff_schema.xsd

非常大的文件。

感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

如果XSD架构包含具有文件系统相对/i属性的xsd:import元素,则schemaLocation函数接受libxmljs.parseXml()选项,可用于设置这些元素的位置

baseUrl

这避免了临时更改工作目录的需要。注意尾随斜线。

答案 1 :(得分:0)

正如您在libxmljs bug tracker上所述,libxmljs在使用导入另一个的模式文件验证XML时会引发错误。

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

这是因为schemaLocation中的相对路径是在进程当前工作目录上计算的。 解决方法正在验证之前更改目录:

fs.readFile(schemaPath, { encoding: 'utf8' }, function (err, xsd) {
    if (err) cb (err);

    var cwd = process.cwd();
    process.chdir(path.dirname(schemaPath));

    var xsdDoc = libxml.parseXml(xsd);
    var xmlDoc = libxml.parseXml(content);

    var output = xmlDoc.validate(xsdDoc);
    process.chdir(cwd);

    cb(undefined, xmlDoc.validationErrors);
});

我不确定libxml如何处理这个问题:也许引用的文件是同步加载的,我认为这不是最理想的。

此解决方法仅适用于本地文件,我不知道如何解决远程schemaLocation,例如示例(schemaLocation="http://www.w3.org/2001/xml.xsd"/>

即使它不是真正的解决方案,我认为这可能有所帮助。

答案 2 :(得分:0)

如其他答案中所述,libxmljs 在使用架构文件验证 XML 时会引发错误,该架构文件 <import> 来自 http 或 https url。但是,如果 <import> 标记引用本地文件 url,libxmljs 可以正常工作。

此解决方案通过下载 xsd 并更新内存中的 xsd 文档以引用本地副本来自动处理 xsd 中的每个 <import> 元素。

它使用 axios 来获取 url,但这可以替换为您最喜欢的请求库。请注意,处理仅深入一层。如果下载的 xsd 也包含 <import> 元素,它们将不会被处理,尽管添加这种类型的递归并不难。

import * as libxml from "libxmljs"
import * as fs from "fs"
import * as os from "os"
import axios from "axios"

async function validateFile(file:string) {
  const fileContents = fs.readFileSync(file).toString()
  const doc = libxml.parseXml(fileContents)
  const root = doc.root()
  const schemaHref = root.namespace().href()
  const schemaLocation = root.attrs().find(a=>a.namespace().href() === 'http://www.w3.org/2001/XMLSchema-instance' && a.name() === "schemaLocation").value()
  const loc = (href:string):string=>{
    const pieces = schemaLocation.split(/\s+/)
    for (let i=0; i<pieces.length; i+=2) {
      if (pieces[i] === href) return pieces[i+1]
    }
  }
  const schemaDoc = libxml.parseXml((await axios.get(loc(schemaHref))).data)
  const importElements = schemaDoc.find("//schema:import",{schema:"http://www.w3.org/2001/XMLSchema"})
  // make any imports local
  for (const e of importElements) {
    const schemaLocation = e.attr("schemaLocation").value();
    const newFileName = os.tmpdir()+"/"+schemaLocation.replace(/[^a-z_.]/ig,'_')
    e.attr({schemaLocation:"file://"+newFileName})
    fs.writeFileSync(newFileName,(await axios.get(schemaLocation)).data)
  }
  if (!doc.validate(schemaDoc)) throw Error(doc.validationErrors.map(e=>(e.message + " at " + e.line + ":" + e.column)).join())
}
const file = "0001.xml"
validateFile(file)