PyXB:创建更严格的匿名类型实例
有没有办法用PyXB从一个包含匿名复杂类型的XSD生成一个模块,并且可以进行一些基本的类型和约束检查呢?
这是我目前的进展:
使用一个第三方(匿名)XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.foo.com" xmlns="http://www.foo.com" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ResultSet">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Bar">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" name="code" type="xs:string"/>
<xs:element minOccurs="0" name="description" type="xs:string"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
<xs:attribute name="href" type="xs:string"/>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
我生成了一个Python模块:
> pyxbgen -u Foo.xsd -m Foo
Python for http://www.foo.com requires 1 modules
> ls
Foo.py Foo.xsd
然后在Python解释器中,我能够创建一个ResultSet
实例,并在Bar
下创建条目:
> python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyxb
>>> import Foo
>>> Foo
<module 'Foo' from 'Foo.py'>
>>> rs = Foo.ResultSet()
>>> rs
<Foo.CTD_ANON object at 0x7f7360834950>
>>> rs.Bar
<pyxb.binding.content._PluralBinding object at 0x7f7360834a90>
>>> rs.Bar.append( pyxb.BIND( name='a', code='b', description ='c' ) )
>>> rs.Bar
<pyxb.binding.content._PluralBinding object at 0x7f39f1018a90>
>>> [x for x in rs.Bar ]
[<Foo.CTD_ANON_ object at 0x7f39f1018b90>]
我可以与rs.Bar[0]
的成员进行交互。
但是让我困扰的是,我也可以:
>>> rs.Bar.append( pyxb.BIND( name='a' ) )
并且它会接受这个,尽管匿名complexType
中的code
元素有一个属性minOccurs="1"
。我从阅读文档和一些其他StackOverflow的问题中了解到,PyXB似乎是在动态反向工程数据类型,这可能解释了它的宽容性。
如果可能的话,我希望在整个过程中添加一些东西,让我可以基本上说:
>>> rs.Bar.append( Something( name='a', code='b' ) )
并且让它对任何缺失的参数提出警告。到目前为止,我考虑过自己编写一些函数,返回带有适当参数的pyxb.BIND()
的结果,但这意味着需要相当多的手动干预。
有没有办法用PyXB自动创建这样的智能检查?
1 个回答
1
PyXB之所以不宽容,并不是因为反向工程的问题,而是因为它在构建元素时不进行验证,也不在将复杂类型分配给元素时进行验证。这是因为可能需要多条Python语句来为元素分配必要的内容:这些内容并不总是可以通过pyxb.BIND()
的参数合理提供。
举个例子,你可以通过以下方式来完成你的代码:
rs.Bar.append( pyxb.BIND( name='a' ) )
rs.Bar[-1].code = '42'
到那时,rs
就会是有效的。
如果你想在对象被添加时进行验证,你应该使用一个中间函数来验证被添加的元素。下面的代码展示了在传入参数可能是未解析的pyxb.BIND
结构时,如何做到这一点:
import pyxb
import Foo
def checkedAppendBar(rs, bar):
rs.Bar.append(bar)
bar = rs.Bar[-1]
try:
bar.validateBinding()
except pyxb.ValidationError as e:
print e.details()
return bar
rs = Foo.ResultSet()
# Fails:
#bar = checkedAppendBar(rs, pyxb.BIND(name='a'))
bar = checkedAppendBar(rs, pyxb.BIND(name='a', code='42'))