【问题标题】:Python XML to recordsPython XML 到记录
【发布时间】:2018-04-18 10:02:22
【问题描述】:

我正在尝试遍历我只对某些元素值/文本感兴趣的嵌套 xml 文件结构。 xml itelf 包含“Row”元素,表示值可以出现多次。目标是将其读取/转换为数据库记录。 xml 看起来像这样:

<CommandManagerResults>
<ListPropertiesAttribute>
    <Row>
        <Name>AttributeName</Name>
        <Id>B31BEF954E05B473A8D3A1B63B29F91E</Id>
        <Description>TECHCOLUMNNAME</Description>
        <LongDescription/>
        <CreationTime>26. August 2010 10:16:10 MESZ</CreationTime>
        <ModificationTime>23. November 2017 20:13:37 MEZ</ModificationTime>
        <Owner>Administrator</Owner>
        <Hidden>False</Hidden>
        <Row>
            <AttributeFormName>ID</AttributeFormName>
            <AttributeFormCategory>ID</AttributeFormCategory>
            <AttributeFormType>Number</AttributeFormType>
            <AttributeFormDescription/>
            <AttributeFormReportSort>None</AttributeFormReportSort>
            <AttributeFormBrowseSort>None</AttributeFormBrowseSort>
            <AttributeLookUpTable>FACT_TABLE_NAME</AttributeLookUpTable>
            <Row>
                <SchemaExpression>ApplySimple("nvl(#0, -2)";TECHCOLUMNNAME)</SchemaExpression>
                <MappingMethod>Manual</MappingMethod>
                <Row>
                    <SchemaCandidateTable>FACT_TABLE_NAME</SchemaCandidateTable>
                </Row>
            </Row>
            <Multilingual>Unknown</Multilingual>
        </Row>
        <Row>
            <AttributeFormName>DESC</AttributeFormName>
            <AttributeFormCategory>DESC</AttributeFormCategory>
            <AttributeFormType>Number</AttributeFormType>
            <AttributeFormDescription/>
            <AttributeFormReportSort>None</AttributeFormReportSort>
            <AttributeFormBrowseSort>None</AttributeFormBrowseSort>
            <AttributeLookUpTable>FACT_TABLE_NAME</AttributeLookUpTable>
            <Row>
                <SchemaExpression>TECHCOLUMNNAME</SchemaExpression>
                <MappingMethod>Manual</MappingMethod>
                <Row>
                    <SchemaCandidateTable>FACT_TABLE_NAME</SchemaCandidateTable>
                </Row>
            </Row>
            <Multilingual>False</Multilingual>
        </Row>
        <Row>
            <AttributeChild>TABLE_PK</AttributeChild>
            <AttributeChildRelationship>One to Many</AttributeChildRelationship>
            <AttributeChildTable>FACT_TABLE_NAME</AttributeChildTable>
            <Path>\Schema Objects\Attributes\FACT_TABLE_NAME\Star Attributes\_technical</Path>
        </Row>
        <Row>
            <Row>
                <AttributeBrowseDisplay>DESC</AttributeBrowseDisplay>
            </Row>
            <Row>
                <ReportDisplayForm>DESC</ReportDisplayForm>
            </Row>
            <AttributeElementDisplay>Locked</AttributeElementDisplay>
            <SecurityFliterToElementBrowsing>True</SecurityFliterToElementBrowsing>
            <EnableElementCaching>True</EnableElementCaching>
        </Row>
    </Row>
</ListPropertiesAttribute>

当把它写成一个程序脚本(我猜不是太 Pythonic)时,一切都按预期工作,但我必须一遍又一遍地重复编写相同的代码:

from lxml import etree
xml = "starattribute_multi.xml"
elem = etree.parse(xml).find("ListPropertiesAttribute")

l=[]
for r in elem.find(".//Row"):

if r.tag == 'Name':
    _name = r.text

elif r.tag == "Id":
    _id = r.text

elif r.getchildren():

    for r1 in r:

        if r1.tag == "AttributeFormName":
            _attr_form = r1.text

        elif r1.tag == "AttributeFormType":
            _attr_form_type = r1.text

        elif r1.tag == "AttributeFormReportSort":
            _form_repsort = r1.text

        elif r1.tag == "AttributeFormBrowseSort":
            _form_browsesort = r1.text

        elif r1.tag == "AttributeLookUpTable":
            _attr_lutable = r1.text

        elif r1.getchildren():

            for r2 in r1:

                if r2.tag == "SchemaExpression":
                    _schema_expr = r2.text

                elif r2.tag == "MappingMethod":
                    _schema_mapping = r2.text

                elif r2.getchildren():

                    for r3 in r2:

                        if r3.tag == "SchemaCandidateTable":
                            _schema_table = r3.text

                        l.append((_name,_id,_attr_form,_attr_form_type,_form_repsort,_form_browsesort,_attr_lutable,_schema_expr,_schema_mapping,_schema_table))

这一切都很好,我得到了我想要的元组列表。 输出如下:

    [('AttributeName',
  'B31BEF954E05B473A8D3A1B63B29F91E',
  'ID',
  'Number',
  'None',
  'None',
  'FACT_TABLE_NAME',
  'ApplySimple("nvl(#0, -2)";TECHCOLUMNNAME)',
  'Manual',
  'FACT_TABLE_NAME'),
 ('AttributeName',
  'B31BEF954E05B473A8D3A1B63B29F91E',
  'DESC',
  'Number',
  'None',
  'None',
  'FACT_TABLE_NAME',
  'TECHCOLUMNNAME',
  'Manual',
  'FACT_TABLE_NAME')]

现在我想稍微正式化一下,以删除我的重复代码并允许我处理其他类似但不相同的 xml。 我想到了编写函数,它可以检查搜索元组中提供的我想要的标签并希望使用字典,以便稍后识别已找到的值。

我的函数如下所示:

def traverse3(xmlelement,searchelements,dictreturn):
_d=dict()
for row in xmlelement:
    if row.getchildren():
        traverse3(row,searchelements,_d)
    else:
        dictreturn[row.tag]=row.text
    dictreturn.update(_d)
return dictreturn

当时的预期用途是:

from lxml import etree
root = etree.parse("some.xml")
l = []
tags = ('Name', 'Id', 'AttributeFormName', 'AttributeFormType', 'AttributeFormReportSort', 'AttributeFormBrowseSort', 'AttributeLookUpTable', 'SchemaExpression', 'MappingMethod','SchemaCandidateTable')
d = {}
l.append(traverse3(elem,tags,d))

我只得到了“最后一个”记录,这肯定是因为我错过了在某处添加一个新的 dict 或早点返回它或我错过的其他任何东西。

[{'Name': 'AttributeName',
  'Id': 'B31BEF954E05B473A8D3A1B63B29F91E',
  'Description': 'TECHCOLUMNNAME',
  'AttributeFormName': 'DESC',
  'AttributeFormType': 'Number',
  'AttributeFormReportSort': 'None',
  'AttributeFormBrowseSort': 'None',
  'AttributeLookUpTable': 'FACT_TABLE_NAME',
  'SchemaExpression': 'TECHCOLUMNNAME',
  'MappingMethod': 'Manual',
  'SchemaCandidateTable': 'FACT_TABLE_NAME']

添加一些打印后,我可以看到我想要的记录(带有 ID 表单的记录)在我的递归调用期间存在,但它被另一个与 DESC 表单有点相似的记录覆盖 - 我想要嗯,当然。 我添加了一些功能,试图减少我的搜索标签列表以具有某种退出条件,但是所有这样做的尝试(甚至移动返回)都以一些“NoneType 不可迭代”结束。

我真的很感激一些想法/方向。

提前为这个史诗般的问题/示例道歉。

【问题讨论】:

    标签: python xml lxml


    【解决方案1】:

    不确定回答我自己的问题是否被视为良好做法。 同时我找到了解决方案,我通过设置/从我感兴趣的最深元素开始并迭代该元素的祖先来反转处理。

        def reverslookup(xmlelement,searchtags):
        d={}
        d[xmlelement.tag] = xmlelement.text
        for parent in xmlelement.iterancestors():
            if parent.tag == "Row":
                for elem in parent:
                    if elem.tag in searchtags:
                        d[elem.tag] = elem.text
        return d
    
    
    if __name__ == "__main__":
        from lxml import etree
        root = etree.parse("file.xml")
        tags = ('Name', 'Id', 'AttributeFormName', 'AttributeFormType', 'AttributeFormReportSort', 'AttributeFormBrowseSort', 'AttributeLookUpTable', 'SchemaExpression', 'MappingMethod','SchemaCandidateTable')
        l=[]
        for start in root.findall(".//*/SchemaCandidateTable"):
            l.append(reverslookup(start,tags))
    

    这样我就可以得到上面的元素,而不必处理 xml 文件中潜在的重复标签。它为我提供了每条记录所需的字典列表:

    Out[6]: 
    
    [{'SchemaCandidateTable': 'FACT_TABLE_NAME',
      'SchemaExpression': 'ApplySimple("nvl(#0, -2)";TECHCOLUMNNAME)',
      'MappingMethod': 'Manual',
      'AttributeFormName': 'ID',
      'AttributeFormType': 'Number',
      'AttributeFormReportSort': 'None',
      'AttributeFormBrowseSort': 'None',
      'AttributeLookUpTable': 'FACT_TABLE_NAME',
      'Name': 'AttributeName',
      'Id': 'B31BEF954E05B473A8D3A1B63B29F91E'},
     {'SchemaCandidateTable': 'FACT_TABLE_NAME',
      'SchemaExpression': 'TECHCOLUMNNAME',
      'MappingMethod': 'Manual',
      'AttributeFormName': 'DESC',
      'AttributeFormType': 'Number',
      'AttributeFormReportSort': 'None',
      'AttributeFormBrowseSort': 'None',
      'AttributeLookUpTable': 'FACT_TABLE_NAME',
      'Name': 'AttributeName',
      'Id': 'B31BEF954E05B473A8D3A1B63B29F91E'}]
    

    【讨论】:

    • 是的,你肯定可以 answer your own question!作为新成员,您必须稍等片刻才能将其标记为“已接受”(这表明您的问题有一个有效的答案),并且可能还会有另一个更好的答案。但这看起来不错。
    • 很高兴知道并感谢您指出这一点@usr2564301。作为新手(之前只能被动阅读),似乎可能会发生各种问题(因为回答重复的问题而被否决)。也感谢您对答案/解决方案本身的评论。
    猜你喜欢
    • 2016-06-05
    • 2015-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-17
    相关资源
    最近更新 更多