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

问题描述

背景情况:带有SCH的XSD

XML模式(XSD)

我有一个XML模式定义(“模式”),其中包括其他几个XSD,它们都在同一个命名空间中。其中一些从外部名称空间导入其他XSD。总而言之,该架构声明了几个可以实例化为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将永远不会返回空节点集。如果XPath中的某个步骤被意外省略或拼写错误等,情况也是如此。这显然不是对这种规则的“正确性”的强烈要求,但它将是第一步。

我寻求解决方案的方法

至少在我看来,针对实际架构,评估为架构的 instance 设计的XPath(更不用说整个XQuery!)似乎不是一个小问题。它如何包含诸如//ancestor::sibling::之类的轴步。因此,我决定构造一个我称为“最大实例” 的东西:递归地遍历所有全局元素及其子元素(以及它们各自的复杂类型的结构等),我在运行时构建了一个XML实例,该实例包含在普通实例中可能存在的所有可能的元素和属性,但在所有情况下一旦。因此,每个可选元素/属性,选择块内的每个元素等等。因此,所说的最大实例看起来像这样:

<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分解成其组件(我想我需要抽象的语法树),以便拥有所有单独的XPath 。例如,当给定XQuery if (abc:a/abc:b = '123') then abc:x/abc:y = ('aaa','ccc') else true()时,我将需要检索abc:a/abc:babc:x/abc:y

我认为可以使用Saxon-HE(或者可能是其他目前不了解的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(...)(这似乎很有希望),但我无法在这里弄清楚参数化的内容。我试过使用XdmNode,以为是Destination?而且,我正在使用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会自动为您完成这项工作:这与支持架构的XQuery处理器尝试执行的操作非常相似。但是另一个区别是,在可识别架构的XQuery中,您不能假定每个名为foo的元素都是该架构中名为foo的元素声明的有效实例。例如,查询将有效实例转换为无效实例,反之亦然,这是相当合法的。毕竟,输入和输出可能符合不同的模式。

Saxon使用路径分析来执行此操作:它查看路径表达式以查看“它们可能导致的位置”。路径分析还用于评估可流动性,并支持文档投影(构建源文档的精简树状表示,从而排除查询无法到达的部分)。 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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...