【问题标题】:How to validate an XSD schema with lxml, but ignore elements that match a given pattern?如何使用 lxml 验证 XSD 架构,但忽略与给定模式匹配的元素?
【发布时间】:2016-12-21 16:42:44
【问题描述】:

可以使用lxml 根据给定的 XSD 架构验证 XML 文件。

有没有办法在不那么严格的意义上应用这种验证,忽略所有包含特殊表达式的元素?

考虑以下示例:假设我有一个 xml_file

<foo>99</foo>
<foo>{{var1}}</foo>
<foo>{{var2}}</foo>
<foo>999</foo>

现在,我在这个文件上运行一个程序,替换{{...}}-表达式并生成一个新文件:

xml_file_new

<foo>99</foo>
<foo>23</foo>
<foo>42</foo>
<foo>999</foo>

到目前为止,我可以使用lxml 来验证新的 XML 文件,如下所示:

from lxml import etree
xml_root = etree.parse(xml_file_new)
xsd_root = etree.parse(xsd_file)
schema = etree.XMLSchema(xsd_root)
schema.validate(xml_root)

我的示例中的相关点是架构将 &lt;foo&gt; 内容限制为整数。

不可能提前在旧的xml_file 上应用架构,但是,由于我的程序执行了一些其他昂贵的任务,我想这样做,同时忽略所有包含任何{{...}}-表达式的行:

<foo>99</foo>       <!-- should be checked-->
<foo>{{var1}}</foo> <!-- should be ignored -->
<foo>{{var2}}</foo> <!-- should be ignored -->
<foo>999</foo>      <!-- should be checked-->

编辑:可能的解决方案:一种想法是定义两个模式

  • 新文件的第二个严格模式,只允许整数
  • 旧文件的宽松模式,允许使用 {{..}}-expressions 的整数和任意字符串

但是,为了避免保持两个模式同步的冗余任务,需要一种方法来自动从严格模式生成宽松模式。这听起来很有希望,因为两种模式具有相同的结构,只是某些元素内容的限制不同。 XSD 是否提供了一个简单的概念,它只允许从一个模式“继承”,然后将额外的放松“附加”到各个元素?

【问题讨论】:

  • 如果不更改 XML 或架构,我认为这是不可能的。由于您无法更改 XML,您是否愿意更改架构?因为你可以为&lt;foo&gt; 定义一个联合类型,它允许either 整数 {{var...}}
  • 谢谢,我已经这样做了。但是,我仍然想在替换后 使用严格的整数检查并禁止{{...}}-表达式。第一次检查只是为了节省时间而预先检测问题,第二次检查很重要。为了确保{{var1}} 在新文件中被一个整数替换,还需要排除{{var1}}new 文件中被{{var3}} 替换的情况。在这里对两个检查使用相同的架构会产生误报。
  • 另一方面,我担心使用两个模式(第一个宽松,第二个更严格)会导致很多冗余,尤其是当我需要非常频繁地更改和更新模式时。跨度>
  • @Meyer 请看我的编辑。

标签: python xml xsd lxml xsd-validation


【解决方案1】:

要回答已编辑的问题,可以使用xs:include(和xs:import)机制来组合模式。这样,您可以在公共模式中声明公共部分以供重用,并为专门的类型定义使用专用模式,如下所示:

描述结构的通用架构。 注意它使用FooType,但没有定义它:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <!-- Example structure -->
  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="foo" type="FooType" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

在替换之前要验证的宽松架构。 它包括来自通用模式的组件, 并定义一个轻松的FooType

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:include schemaLocation="common.xsd"/>

  <xs:simpleType name="FooType">
    <xs:union memberTypes="xs:integer">
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:pattern value="\{\{.*\}\}"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:union>
  </xs:simpleType>

</xs:schema>

替换后要验证的 strict 架构。它 定义了FooType的严格版本:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:include schemaLocation="common.xsd"/>

  <xs:simpleType name="FooType">
     <xs:restriction base="xs:integer"/>
  </xs:simpleType>

</xs:schema>

为了完成,还有其他方法可以做到这一点, 例如 xs:redefine (XSD 1.0) 或 xs:override (XSD 1.1)。 但是这些具有更复杂的语义,就我个人而言,我尽量避免使用它们。

【讨论】:

  • 感谢您的明确示例。我认为它还没有解决冗余问题,因为&lt;foo&gt;&lt;xs:union memberTypes="xs:integer"&gt; 的严格限制应该只发生一次。你的解决方案能在这个意义上得到改进吗?我想到了类似“附加”严格模式的放松。我对 XSD 很陌生,但在继承方面,我宁愿从strict派生 relaxed,而不是从抽象基common 派生两者。
  • 我喜欢你的回答是它已经在结构和内容的限制之间实现了清晰的分离。
  • @flonk,问题是simple types只能限制,不能扩展。所以你不能从严格到放松。
  • 好的,我明白了。我的观点是避免使用公共基础,而是使用 direct 继承来避免重复整数限制,因为这是两种方案的共同属性。所以在我的短语“从严格到放松”中,方向是不相关的,“从放松到严格”也可以解决我的问题。因此,如果您说后者是可能的,您是否可以通过添加\{\{.*\}\}-restriction 从宽松方案中得出严格?
  • 反过来也很困难。 AFAIK,不可能将联合类型限制回其各个组件。一种可能性是使用正则表达式而不是联合,然后你可以限制它。但是任何限制都将再次包含整数类型的重复。我将使用替代解决方案添加另一个答案。
【解决方案2】:

仅使用普通 XSD,我不知道有什么方法可以避免 整数类型的冗余声明。然而, 作为替代方案,您可以在 Python 中调整架构。

一种简单的方法是,只使用一个模式文档(默认为宽松):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="foo" type="FooType" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:simpleType name="FooType">
    <xs:union memberTypes="xs:integer">
      <xs:simpleType id="RELAXED">
        <xs:restriction base="xs:string">
          <xs:pattern value="\{\{.*\}\}"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:union>
  </xs:simpleType>

</xs:schema>

在 Python 中,您可以简单地删除带有 id="RELAXED" 的元素以创建严格模式:

from lxml import etree

xsd_tree = etree.parse("relaxed.xsd")
xml_tree = etree.parse("test.xml")

# Create default relaxed schema
relaxed_schema = etree.XMLSchema(xsd_tree)

# Remove RELAXED element to create strict schema
pattern = xsd_tree.find(".//*[@id='RELAXED']")
pattern.getparent().remove(pattern)
strict_schema = etree.XMLSchema(xsd_tree)

print("Relaxed:", relaxed_schema.validate(xml_tree))
print("Strict:", strict_schema.validate(xml_tree))

当然,使用 Python,您可以通过多种不同的方式来实现这一点。例如,您还可以动态生成xs:union 元素并将其插入到模式的严格版本中。但这会变得更加复杂。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-19
    • 2018-02-09
    • 1970-01-01
    • 2015-02-25
    • 2011-06-18
    • 2019-12-13
    • 2010-11-20
    • 1970-01-01
    相关资源
    最近更新 更多