0

我正在尝试在命令行上运行 saxon-js 以应用当前在另一个使用 Saxon HE 的系统中工作的 XSL 3 转换,因为 saxon-js 看起来可以提供更多的多功能性。

我基本上是 XSL 的新手,所以学习曲线很陡峭。

我目前遇到的错误是这样的:

转换失败:iati.xslt#90 处出现错误 FODC0002 未知集合(未提供 collectionFinder)

触发此操作的 XSLT 片段是:

  <xsl:variable name="iati-codelists">
    <codes version="2.03">
      <xsl:apply-templates select="collection('../lib/schemata/2.03/codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
      <xsl:apply-templates select="collection('../lib/schemata/non-embedded-codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
    </codes>
  </xsl:variable>

这打算转到该目录并清理一组 .xml 文件。

查看 saxon-js 文档,我看不到提供集合查找器的选项。

这是在 Saxon HE(目前正在做这项工作)中实现的东西,而不是目前在 Saxon-Js 中实现的东西吗?还是我在吠叫一棵不同但同样错误的树?

谢谢!

4

1 回答 1

1

Yes you can. However the collectionFinder doesn't seem to work asynchronously so it's not super useful if you are writing an asynchronous application.

I was able to get this to work with some hardcoding of the paths provided in the collections() in my node app as a proof of concept. Definitely is a better way to do this.

If this is your XML

<xsl:variable name="iati-codelists">
    <codes version="2.03">
      <xsl:apply-templates select="collection('../lib/schemata/2.03/codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
      <xsl:apply-templates select="collection('../lib/schemata/non-embedded-codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
    </codes>
    <codes version="2.02">
      <xsl:apply-templates select="collection('../lib/schemata/2.02/codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
      <xsl:apply-templates select="collection('../lib/schemata/non-embedded-codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
    </codes>
    <codes version="2.01">
      <xsl:apply-templates select="collection('../lib/schemata/2.01/codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
      <xsl:apply-templates select="collection('../lib/schemata/non-embedded-codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
    </codes>
    <codes version="1.05">
      <xsl:apply-templates select="collection('../lib/schemata/1.05/codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
    </codes>
    <codes version="1.04">
      <xsl:apply-templates select="collection('../lib/schemata/1.04/codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
    </codes>
    <codes version="1.03">
      <xsl:apply-templates select="collection('../lib/schemata/1.03/codelist/?select=*.xml;recurse=yes')" mode="get-codelists"/>
    </codes>
  </xsl:variable>

Before the transform runs this code creates an object of the collections. The keys are a part of the file path to the codelist directories which contain a series of xml docs. The values are Arrays of the xml documents converted into the format Saxon JS needs with SaxonJS.getResource(). It got a little tricky with having the Promises inside the object so I had to use Lodash.

const _ = require('lodash');
const fs = require('fs');
const fsPromises = fs.promises;

const SaxonJS = require('saxon-js');

// load codelists since collectionFinder can't be async
let codelistPaths = [
    "non-embedded-codelist/",
    "2.03/codelist/",
    "2.02/codelist/",
    "2.01/codelist/",
    "1.05/codelist/",
    "1.04/codelist/",
    "1.03/codelist/"
];
                
// this returns an object of the codelistPaths as Keys and an Array of resolved promises for the Values. these promises are grabbing the codelist XML files using SaxonJS.getResource
let resources = _.zipObject(codelistPaths, await Promise.all(_.map(codelistPaths, async (path) => {
    let files = await fsPromises.readdir("./IATI-Rulesets/lib/schemata/" + path);
    return await Promise.all(files.map(async (file) => {
        return await SaxonJS.getResource({ type : 'xml', file : "./IATI-Rulesets/lib/schemata/" +  path + file })
    }))
})))         

// this pulls the right array of SaxonJS resources from the resources object
const collectionFinder = (url) => {
    if (url.includes("codelist")) {
        let path = url.split('schemata/')[1].split('?')[0]; // get the right filepath (remove file:// and after the ?
        return resources[path]
    } else {
        return []
    }
}

// Applying the XSLT3 Ruleset to IATI Files Using SaxonJS
let results = await SaxonJS.transform({
    stylesheetFileName: "path to your .sef.json",
    sourceFileName: "path to your input .xml",
    destination: "serialized",
    collectionFinder: collectionFinder
}, "async")

Full details in the Saxon-JS support forum: https://saxonica.plan.io/issues/4797?pn=1#change-16579

于 2020-10-22T11:01:30.300 回答