使用 XSD 中的属性字段获取标签的路径

Posted

技术标签:

【中文标题】使用 XSD 中的属性字段获取标签的路径【英文标题】:Get path of tags using attribute field in XSD 【发布时间】:2021-12-08 22:03:48 【问题描述】:

我当前的任务是从 XSD 文件中获取信息(字段类型、字段名称等)。我的 XSD 文件看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<!-- edited with XMLSpy v2018 rel. 2 sp1 (x64) (http://www.altova.com) by test (123321) -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:complexType name="attribute">
        <xs:annotation>
            <xs:documentation>Атрибуты ОГХ</xs:documentation>
        </xs:annotation>
        <xs:sequence>
            <xs:element name="owner_id">
                <xs:annotation>
                    <xs:documentation>Данные о балансодержателе</xs:documentation>
                </xs:annotation>
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="legal_person" type="xs:integer">
                            <xs:annotation>
                                <xs:documentation>ID балансодержателя</xs:documentation>
                            </xs:annotation>
                        </xs:element>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="snow_clean_area" type="xs:double">
                <xs:annotation>
                    <xs:documentation>Площадь вывоза снега, кв. м</xs:documentation>
                </xs:annotation>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

如我们所见,有一些字段 与其他 内部(嵌套)。

我需要获取该 XSD 中所有元素的名称。但是如果一个元素在另一个元素中,我需要将名称写为“all_prev_names;cur_name”。对于我之前展示的 XSD,它将是:

"owner_id;legal_person"
"snow_clean_area"

要进行更多嵌套,名称必须包含所有以前的名称。

我写了那个代码:

        def recursive(xml, name=None):
            res = xml.find_all('xs:element')

            if res:
                for elem in res:
                    if name:
                        yield from recursive(elem, elem['name'] + ';' + name)
                    else:
                        yield from recursive(elem, elem['name'])
            else:
                if name:
                    yield (name)
                else:
                    yield (xml['name'])

但是重复路径存在问题。该函数的结果将是:

"owner_id;legal_person"
"legal_person"
"snow_clean_area"

我需要修复该代码,或者获得另一个想法,如何解决该任务。

【问题讨论】:

您可以尝试使用xml2xpath.sh 从xsd 生成一个xml 并获取XPath 表达式:xml2xpath.sh -a -f shiporder -d tests/resources/shiporder.xsd。需要xmlbeans 包。 【参考方案1】:

如果您想处理任何 XSD,这将是一个非常艰巨的挑战,因为 XSD 作者可以通过多种不同的方式为您解决问题 - 类型限制和扩展、替换组、命名模型组和属性组、xsd:import、xsd:redefine 等。另一方面,如果您只需要处理一个模式,那么您就不会这样做;所以你必须决定允许多少变化。

使用已经使用模式处理器处理过的编译模式通常比使用源 XSD 文件要容易得多,并且会处理许多可以用不同的方式编写相同内容的变体方法。例如,编译后的模式可能会扩展替换组,就好像它是使用 xsd:choice 编写的一样。

鉴于您身处 Python 世界,一种方法是使用 Saxon 模式处理器将源模式编译为 SCM 文件(SCM = 模式组件模型)。 SCM 文件仍然是 XML,但它被扁平化和规范化,应用程序更容易从中提取信息。

(我不知道 xmlproc 是否有允许您访问已编译模式的 API - 如果有,那将是另一种方法。)

请注意,如果您尝试生成诸如 owner_id;legal_person 之类的路径,则架构可以是递归的并允许无限嵌套,因此这种方法可能会导致您尝试生成无限路径(这可能会因堆栈而失败溢出)。您还需要注意通配符 (xs:any)。

【讨论】:

【参考方案2】:

使用xml2xpath.sh 从 xsd 生成 xml 并获取 XPath 表达式:xml2xpath.sh -a -f root -d test.xsd。需要xmlbeans 包。

提供的示例不能开箱即用,但下面的示例可以。 xsd2inst 来自 xmlbeans 包状态的实用程序帮助

根据给定的 Schema 文件生成一个文档 以给定元素为根。 该工具会做出合理的尝试来创建有效的文档, 但这并不总是可能的,例如, 存在没有有效实例文档的模式 可以生产。

鉴于此 XSD

<?xml version="1.0" encoding="UTF-8"?>
<!-- edited with XMLSpy v2018 rel. 2 sp1 (x64) (http://www.altova.com) by test (123321) -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="root">
    <xs:complexType>
        <xs:annotation>
            <xs:documentation>Атрибуты ОГХ</xs:documentation>
        </xs:annotation>
        <xs:sequence>
            <xs:element name="owner_id">
                <xs:annotation>
                    <xs:documentation>Данные о балансодержателе</xs:documentation>
                </xs:annotation>
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="legal_person" type="xs:integer">
                            <xs:annotation>
                                <xs:documentation>ID балансодержателя</xs:documentation>
                            </xs:annotation>
                        </xs:element>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="snow_clean_area" type="xs:double">
                <xs:annotation>
                    <xs:documentation>Площадь вывоза снега, кв. м</xs:documentation>
                </xs:annotation>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>
</xs:schema>

实用程序将返回

xml2xpath.sh -a -f root -d test.xsd 
Creating XML instance starting at element root from test.xsd

xml2xpath: find XPath expressions on /tmp/tmp.FJQYKaDZI0
================================================================================ (2021-10-22 16:39:09 -03)

   -a ; 'abs_path=1'
   -f ; 'tag1=root'
   -d
================================================================================ (2021-10-22 16:39:09 -03)

Namespaces: None
================================================================================ (2021-10-22 16:39:09 -03)

Elements to process (build xpath, add prefix) 4

XPath expressions found: 4 (absolute, unique elements, use -r to override)
================================================================================ (2021-10-22 16:39:09 -03)

/root
/root/owner_id
/root/owner_id/legal_person
/root/snow_clean_area


received EXIT, bye!
================================================================================ (2021-10-22 16:39:09 -03)

xmllint 和 xpath 也可用于获取 name, type 属性,但需要更多解析

(echo "setrootns"; echo "xpath //xs:element/@*" ; echo "bye") | xmllint --shell test.xsd
/ > setrootns
/ > xpath //xs:element/@*
Object is a Node Set :
Set contains 6 nodes:
1  ATTRIBUTE name
    TEXT
      content=root
2  ATTRIBUTE name
    TEXT
      content=owner_id
3  ATTRIBUTE name
    TEXT
      content=legal_person
4  ATTRIBUTE type
    TEXT
      content=xs:integer
5  ATTRIBUTE name
    TEXT
      content=snow_clean_area
6  ATTRIBUTE type
    TEXT
      content=xs:double
/ > bye

另类

(echo "setrootns"; echo "cat //xs:element/@*" ; echo "bye") | xmllint --shell test.xsd
/ > setrootns
/ > cat //xs:element/@*
 -------
 name="root"
 -------
 name="owner_id"
 -------
 name="legal_person"
 -------
 type="xs:integer"
 -------
 name="snow_clean_area"
 -------
 type="xs:double"
/ > bye

【讨论】:

【参考方案3】:

我找到了适合我的解决方案。我使用 ElementTree.iterparse,而不是 BeautifulSoup。然后,在每个元素之后我保存我的字段,并在标签的末尾,将它保存到我的结构中:

def getXsd(self, typeNumber: int) -> t.List[t.Dict[str, str]]:
    paths = []
    for elem in self.xsds:
        if elem[0] == typeNumber:
            events = ("start", "end")
            codes = []
            type_field = None
            for event, elem in ET.iterparse(BytesIO(elem[1].encode("UTF-8")), events=events):
                if event == 'start' and elem.tag == 'http://www.w3.org/2001/XMLSchemaelement':
                    codes.append(elem.attrib['name'])
                    if 'type' in elem.attrib:
                        type_field = elem.attrib['type']
                elif event == 'start' and elem.tag == 'http://www.w3.org/2001/XMLSchemadocumentation':
                    if codes and type_field:
                        paths.append('code': "".join([str(item).capitalize() for item in codes[::-1]]),
                                     'type': type_field,
                                     'name': elem.text)
                        type_field = None

                elif event == 'end' and elem.tag == 'http://www.w3.org/2001/XMLSchemaelement':
                    codes.pop()
    return paths

结果是:

['code': 'Legal_personOwner_id', 'type': 'xs:integer', 'name': 'ID балансодержателя', 'code': 'Legal_personCustomer_id', 'type': 'xs:integer', 'name': 'ID заказчика', 'code': 'Improvement_object_categoryImprovement_object_category_id', 'type': 'xs:integer', 'name': 'Код категории озеленения', 'code': 'Legal_personDepartment_id', 'type': 'xs:integer', 'name': 'ID ведомственного ОИВ', 'code': 'Snow_clean_area', 'type': 'xs:double', 'name': 'Площадь вывоза снега, кв. м', 'code': 'Reservoir_area', 'type': 'xs:double', 'name': 'Водоемы, кв. м']

【讨论】:

以上是关于使用 XSD 中的属性字段获取标签的路径的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 XSD 中定义为属性时,自动生成的类中的字段会序列化为元素?

在Visual Studio中的MSBuild社区任务BeforeBuild步骤期间找不到路径中的xsd.exe

如何在graphdb lucene连接器中使用属性路径

xpath 轴定位表达方式

HTML中img标签的src填本地绝对路径无法显示

js可以把type=‘file’标签中的文件转换成二进制吗?怎么转换?