如何使用 SaxonJS 进行模式验证以获取缺失的引用

Posted

技术标签:

【中文标题】如何使用 SaxonJS 进行模式验证以获取缺失的引用【英文标题】:How to do schema validation to get missing references with SaxonJS 【发布时间】:2021-10-05 01:40:09 【问题描述】:

如果我有一个简单的xsd文件和一个简单的xml文件,SaxonJS能否显示xml中哪些元素和哪些属性没有在xsd中定义?

我一直在四处寻找示例,但到目前为止一无所获。

更新

我还将接受带有 js 代码(节点)的答案,该代码使用 saxon-js 遍历 xml 资源并检查 xsd 资源中的元素和属性(不必检查属性值)。

以某种有效的方式。

【问题讨论】:

(基于模式的)验证不是 Saxon-JS 的一个特性,据我所知。基于模式的验证是 Saxon EE for Java、.NET 或 C/C++ 提供的,但 Saxon-JS 不提供。 您能展示一个小而有代表性的架构和实例示例吗? “简单的 xsd 文件”说起来容易,但在我看来,大多数模式并不“简单”,嵌套和组合各种类型选项的强大功能和灵活性,使得在 XSLT 中很难想出简单易行的东西。我认为 Saxonica 有一个在 XSLT 中实现的内部模式验证器,但我不知道他们是否愿意将其商业化,以及 Saxon-JS 是否可以处理它。如果这里没有人出现,直接通过saxonica.plan.io/projects/saxon-js 与他们联系可能是有意义的。 此外,“未定义 xml 中的哪些元素和哪些属性”是模糊的,使用模式中的键来查找 foo 的任何 foo 元素声明可能很容易在实例示例中,但这并不能真正说明该特​​定声明是否在正确的位置以使实例元素有效。 @MartinHonnen 您是说 saxon-js 不会将 xsd 资源加载为 xsd 吗?但就像 xml?我假设一旦加载了 xsd,您就可以在某种树结构中查找元素 我不确定您在 XPath 3.1 和 XSLT 3.0 实现(如 Saxon-JS)中期望或看到 XSD 处理的哪个位置,除非它支持 Saxon-JS 不支持的模式感知 XSLT 和 XPath,据我所知。因此,像使用 XSLT/XPath 的任何 XML 文档一样解析/处理 XSD 似乎是可能的,但肯定不能作为替代模式验证器或模式对象模型的综合工具。 【参考方案1】:

要简单地检查架构中是否存在xs:element 元素,一个键就足够了,xs:attribute 也是如此。但所有这些都依赖于简单的 xs:element name="foo"xs:attribute name="att1" 声明,并且绝不会检查嵌套或结构:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="xml" indent="yes"/>
  
  <xsl:key name="element-by-name" match="xs:element" use="QName(/*/@targetNamespace, @name)"/>
  
  <xsl:key name="attribute-by-name" match="xs:attribute" use="QName(/*/@targetNamespace, @name)"/>
  
  <xsl:template match="*[not(key('element-by-name', node-name(), $schema-doc))]">
    <element-not-declared>
      <name>node-name()</name>
      <path>path()</path>
    </element-not-declared>
    <xsl:next-match/>
  </xsl:template>
  
  <xsl:template match="@*[not(key('attribute-by-name', node-name(), $schema-doc))]">
    <attribute-not-declared>
      <name>node-name()</name>
      <path>path()</path>
    </attribute-not-declared>
  </xsl:template>

  <xsl:mode on-no-match="shallow-skip"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:next-match/>
    <xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with system-property('xsl:product-name') system-property('xsl:product-version') system-property('Qhttp://saxon.sf.net/platform')</xsl:comment>
  </xsl:template>
  
  <xsl:param name="schema-doc">
    <xs:schema>
      <xs:element name="root">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="items">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="item" maxOccurs="unbounded">
                    <xs:complexType>
                      <xs:element name="foo" type="xs:string"/>
                    </xs:complexType>
                  </xs:element>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
  </xsl:param>

</xsl:stylesheet>

示例输入

<?xml version="1.0" encoding="utf-8"?>
<root>
  <items count="1">
    <item>
      <foo>foo 1</foo>
      <bar>bar 1</bar>
    </item>
  </items>
</root>

在浏览器中使用 Saxon-JS 2.3 运行时会给出

    <attribute-not-declared>
       <name>count</name>
       <path>/Qroot[1]/Qitems[1]/@count</path>
    </attribute-not-declared>
    <element-not-declared>
       <name>bar</name>
       <path>/Qroot[1]/Qitems[1]/Qitem[1]/Qbar[1]</path>
    </element-not-declared>
    <!--Run with Saxon-JS 2.3 Browser-->

但我已经测试过它也适用于“Saxon-JS 2.3 Node.js”。

因此,这会根据模式中使用的键找到一些未找到匹配声明的元素或属性。这只是一种肤浅的方法,甚至没有考虑命名空间和 elementForm 或 attributeForm 和命名空间的复杂性。

要使用 SaxonJS 运行 XSLT 3 代码,您可以使用 SaxonJS.XPath.evaluate 调用 XPath 3.1 的 transform 函数来运行 XSLT,如下所示,或者您可以先使用 xslt3 命令行工具来导出 XSLT到 SEF/JSON,然后可以使用 SaxonJS.transform 运行。

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

const xml = `<?xml version="1.0" encoding="utf-8"?>
<root>
  <items count="1">
    <item>
      <foo>foo 1</foo>
      <bar>bar 1</bar>
    </item>
  </items>
</root>`;

const xsd = `<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="xml" indent="yes"/>
  
  <xsl:key name="element-by-name" match="xs:element" use="QName(/*/@targetNamespace, @name)"/>
  
  <xsl:key name="attribute-by-name" match="xs:attribute" use="QName(/*/@targetNamespace, @name)"/>
  
  <xsl:template match="*[not(key('element-by-name', node-name(), $schema-doc))]">
    <element-not-declared>
      <name>node-name()</name>
      <path>path()</path>
    </element-not-declared>
    <xsl:next-match/>
  </xsl:template>
  
  <xsl:template match="@*[not(key('attribute-by-name', node-name(), $schema-doc))]">
    <attribute-not-declared>
      <name>node-name()</name>
      <path>path()</path>
    </attribute-not-declared>
  </xsl:template>

  <xsl:mode on-no-match="shallow-skip"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:next-match/>
    <xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with system-property('xsl:product-name') system-property('xsl:product-version') system-property('Qhttp://saxon.sf.net/platform')</xsl:comment>
  </xsl:template>
  
  <xsl:param name="schema-doc">
    <xs:schema>
      <xs:element name="root">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="items">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="item" maxOccurs="unbounded">
                    <xs:complexType>
                      <xs:element name="foo" type="xs:string"/>
                    </xs:complexType>
                  </xs:element>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
  </xsl:param>

</xsl:stylesheet>`;


const result = SaxonJS.XPath.evaluate(`
  transform(
    map 
      'source-node' : parse-xml($xml),
      'stylesheet-text' : $xsd,
      'delivery-format' : 'serialized'
      
  )?output`,
  [],
  
    params :  xml : xml, xsd : xsd 
  
);

console.log(result);

输出

<?xml version="1.0" encoding="UTF-8"?>
<attribute-not-declared>
   <name>count</name>
   <path>/Qroot[1]/Qitems[1]/@count</path>
</attribute-not-declared>
<element-not-declared>
   <name>bar</name>
   <path>/Qroot[1]/Qitems[1]/Qitem[1]/Qbar[1]</path>
</element-not-declared>
<!--Run with Saxon-JS 2.3 Node.js-->

【讨论】:

老实说:我是在寻找“js代码(节点)” 我还没有感谢您的(广泛)回答。谢谢!这很有帮助。不幸的是,当我看到你添加了 js 代码时,我无法再奖励赏金了。

以上是关于如何使用 SaxonJS 进行模式验证以获取缺失的引用的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 saxonjs 将 json 转换为 xml?

SaxonJS 无法与 webpack 一起使用

如何使用自定义策略模式实现 jwt 令牌基础身份验证以在 .net 核心中进行授权?

如何从 Validated Spring RestController 获取多个缺失请求参数的详细信息?

如何在ggplot2中填充空单元格以获取缺失数据

如何通过与模型比较将特定的缺失字段添加到 json 对象