在 C# 中使用 Saxon-HE 查找 XQuery 中的所有 XPath

Finding all XPaths in a XQuery using Saxon-HE with C#

情境背景:XSD 与 SCH

XML 架构 (XSD)

我有一个 XML 模式定义(“模式”),其中包括其他几个 XSD,它们都在同一个命名空间中。其中一些从外部名称空间导入其他 XSDs。总而言之,模式声明了几个可以实例化为 XML 文档的全局元素。我们称它们为 Global_1Global_2Global_3.

业务规则 (SCH)

模式由定义“业务规则”的 Schematron 文件扩充。它定义了一些抽象规则,每个抽象规则都包含一些使用通过XSD定义的数据模型的断言。例如:

<sch:pattern>
    <sch:rule id="rule_A" abstract="true">
        <sch:assert test="if (abc:a/abc:b = '123') then abc:x/abc:y = ('aaa', 'bbb', 'ccc') else true()" id="A-01">Error message</sch:assert>
        <sch:assert test="not(abc:c = 'abcd' and abc:d = 'zz')" id="A-02">Some other error message</sch:assert>
    </sch:rule>
<!-- (...) -->
</sch:pattern>

每个抽象规则都由一个或多个非抽象(具体)规则扩展,这些规则定义了要验证抽象规则断言的特定上下文。例如:

<sch:pattern>
    <!-- (...) -->
    <sch:rule context="abc:Global_1/abc:x/abc:y">
        <sch:extends rule="rule_A"/>
    </sch:rule>
    <sch:rule context="abc:Global_2/abc:j//abc:k/abc:l">
        <sch:extends rule="rule_A"/>
    </sch:rule>
    <!-- (...) -->
</sch:pattern>

换句话说,摘要 rule_A 中定义的所有断言都应用于它们的特定上下文。

“架构”和“业务规则”都可能发生变化 - 我的程序在 运行 时获取它们,而我在设计时不知道它们的内容。我唯一可以安全地假设的是模式中没有无穷无尽的递归结构:每种类型总是有一个确定的叶节点,并且没有类型包含自身。换句话说,实例中没有可能的“无限循环”。

我要解决的问题

基本上,我想以编程方式评估每个定义的规则是否正确。由于正确性可能是一个很有问题的话题,这里我所说的正确性只是指:规则中使用的每个 XPath(即它的上下文和它继承的断言的 XQueries 中)都是“可能的”,这意味着它可以根据到模式中定义的数据模型。 例如,如果忘记了名称空间前缀(abc:a/b 而不是 abc:a/abc:b),则此 XPath 永远不会 return 任何其他而不是空节点集。如果 XPath 中的某个步骤被意外遗漏或拼写错误等,情况也是如此。这显然不是对此类规则“正确性”的非常有力的声明,但它可以作为第一步。

我对此的解决方案

至少对我来说,评估为模式的 实例 设计的 XPath(更不用说整个 XQuery!)似乎不是一个微不足道的问题实际模式,考虑到它可能包含 //ancestor::sibling:: 等轴步骤。所以我决定构建一些我称之为 " 最大实例的东西“:通过递归遍历所有全局元素及其子元素(以及它们各自复杂类型的结构等),我在 运行 时构建了一个 XML 实例,其中包含每个可能的元素和属性在正常实例中的位置,但一次全部。所以每个可选 element/attribute,选择块中的每个元素等等。所以,最大实例看起来像这样:

<maximumInstance>
    <Global_1>
        <abc:a>
            <abc:b additionalAttribute="some_fixed_value">
                <abc:j/>
                <abc:k/>
                <abc:l/>
            </abc:b>
        </abc:a>
    </Global_1>
    <Global_2>
        <abc:x>
            <abc:y>
                <abc:a/>
                <abc:z>
                    <abc:l/>
                </abc:z>
            </abc:y>
        </abc:x>
    </Global_2>
    <Global_3>
        <!-- ... -->
    </Global_3>
    <!-- ... -->
</maximumInstance>

现在所要做的就是遍历所有抽象规则:对于每个抽象规则中的每个断言,必须检查每个上下文是否扩展了相应的抽象规则,断言中的每个 XPath 都会导致非- 根据最大​​实例评估时的空节点集。

我被困在哪里

我编写了一个 C# (.NET Framework 4.8) 程序,将“模式”解析为所述“最大实例”(在 运行 时间是 XDocument)。它还将业务规则解析为一个结构,可以轻松获取每个抽象规则、它的断言以及这些断言要验证的上下文。

但是目前,我只有每个完整的 XQuery(就像它们在 Schematron 文件中一样)可以有效地创建一个断言。但我实际上需要 将 XQuery 分解成它的组件(我想我需要抽象语法树) 这样我就会拥有所有单独的 XPaths.例如,当给定 XQuery if (abc:a/abc:b = '123') then abc:x/abc:y = ('aaa', 'bbb', 'ccc') else true() 时,我需要检索 abc:a/abc:babc:x/abc:y.

我认为这可以使用 Saxon-HE 来完成(或者可能是另一个 Parser/Compiler 目前可用于 C# 我不知道)。不幸的是,我还没有完全理解如何充分利用 Saxon,甚至无法为我想要实现的目标找到至少一个有效的起点。我一直在尝试使用似乎可以通过 XQueryExecutable:

访问的抽象语法树(这样我就可以在 XQuery 中访问相应的 XPath)
Processor processor = new Processor();
XQueryCompiler xqueryCompiler = processor.NewXQueryCompiler();
XQueryExecutable exe = xqueryCompiler.Compile(xquery);
var AST = exe.getUnderlyingCompiledQuery();

var st = new XDocument();
st.Add(new XElement("root"));
XdmNode node = processor.NewDocumentBuilder().Build(st.CreateReader());            
AST.explain((node); // <-- this is an error!

但这对我没有任何帮助:我没有找到任何可以使用的公开属性?虽然 VS 允许我使用 AST.explain(...)(这看起来很有希望),但我无法弄清楚要在这里参数化什么。我尝试使用我认为是 Destination 的 XdmNode?而且,我正在使用 Saxon 10(通过 NuGet),而 Destination 似乎来自 Saxon 9:net.sf.saxon.s9api.Destination?!

有好心人通读了所有这些内容,对我如何解决这个问题有什么建议吗? :-) 或者,也许有更好的方法来解决我没有想到的问题 - 我也很感谢您的建议。

TL;DR

对不起文字墙!简而言之:我有 Schematron 规则,可以使用业务逻辑扩充 XML 模式。要在没有实际 XML 个实例的情况下评估 这些规则 (不是:根据规则验证实例!),我需要分解构成 Schematron 的 XQueries断言到它们的组件中,这样我就可以处理它们中使用的所有 XPath。我认为这可以用 Saxon-HE 来完成,但我的知识太有限,甚至无法理解这是一个很好的起点。 我也愿意就可能更好的方法来解决我的问题提出建议实际问题(如上所述)。

感谢您花时间阅读本文。

如果这是一个 XSD 模式而不是 Schematron 模式,那么 Saxon-EE 会自动为您完成这项工作:这与 schema-aware XQuery 处理器试图做的非常相似做。但另一个区别是,在 schema-aware XQuery 中,您不能假设每个名为 foo 的元素都是模式中名为 foo 的元素声明的有效实例;例如,将有效实例转换为无效实例的查询是非常合法的,反之亦然。毕竟,输入和输出可能符合不同的模式。

Saxon 使用路径分析来执行此操作:它查看路径表达式以查看“它们可能通向何处”。路径分析还用于评估可流动性,并支持文档投影(构建源文档的 trimmed-down 树表示,省略查询无法到达的部分)。 Saxon 中的路径分析绝不是完整的,例如它不会尝试处理递归函数。尽管所有这些操作都需要 Saxon-EE,但基本路径分析代码实际上存在于 Saxon-HE 中,但我不保证它可以用于描述以外的任何目的。

你基本上是对的,这是你自己设置的一个难题,祝你好运。

您可以采用的另一种不涉及绕过 Saxon 内部结构的方法是将 XQuery 转换为 XQueryX,它是解析树的 XML 表示,然后检查 XQueryX(大概使用XQuery) 找到你需要的部分。

虽然 XQueryX(正如 Michael Kay 所指出的)在理论上正是我所寻找的,但不幸的是,在我的研究过程中,我找不到任何关于 .NET 实现的有用信息。

所以我最终通过 创建我自己的解析器 并使用 XPath3.1 grammar for ANTLR4 作为理想的起点解决了整个问题。这样,我现在能够检索任何 Schematron 规则表达式的语法树,从而允许我分别提取每个包含的 XPath 表达式(及其子表达式)。

请注意,另一个绊脚石是 .NET 仍然(!)只真正处理 XPath 1.0:虽然我的解析器按预期执行所有操作,但对于一些找到的表达式,.NET 给了我“非法令牌”尝试评估它们时的错误。安装 XPath2 NuGet package by Chertkov/Heyenrath 是解决方案。