代码之家  ›  专栏  ›  技术社区  ›  Floren

使用ElementTree将XML文件转换为字典

  •  2
  • Floren  · 技术社区  · 10 月前

    我有一个旧软件使用的XML配置文件,我无法更改或格式化。目标是使用Python 3.9并将XML文件转换为字典,仅使用 xml.etree.ElementTree 图书馆。

    我最初是在看这个 reply ,这几乎产生了预期的结果。

    Scenario.xml 文件内容:

    <Scenario Name="{{ env_name }}">
        <Gateways>
            <Alpha Host="{{ host.alpha_name }}" Order="1">
               <Config>{{ CONFIG_DIR }}/alpha.xml</Config>
               <Arguments>-t1 -t2</Arguments>
            </Alpha>
            <Beta Host="{{ host.beta_name }}" Order="2">
               <Config>{{ CONFIG_DIR }}/beta.xml</Config>
               <Arguments>-t1</Arguments>
            </Beta>
            <Gamma Host="{{ host.gamma_name }}" Order="3">
               <Config>{{ CONFIG_DIR }}/gamma.xml</Config>
               <Arguments>-t2</Arguments>
               <!--<Data Count="58" />-->
            </Gamma>
        </Gateways>
    </Scenario>
    

    将XML文件转换为字典的Python代码:

    from pprint import pprint
    from xml.etree import ElementTree
    
    
    def format_xml_to_dictionary(element: ElementTree.Element):
        '''
        Format xml to dictionary
    
        :param element: Tree element
        :return: Dictionary formatted result
        '''
        try:
            return {
                **element.attrib,
                '#text': element.text.strip(),
                **{i.tag: format_xml_to_dictionary(i) for i in element}
            }
        except ElementTree.ParseError as e:
            raise e
    
    if __name__ == '__main__':
        tree = ElementTree.parse('Scenario.xml').getroot()
        scenario = format_xml_to_dictionary(tree)
        pprint(scenario)
    

    功能代码输出 <!--<Data Count="58" />--> 评论:

    $ python test.py
    {'#text': '',
     'Gateways': {'#text': '',
                  'Alpha': {'#text': '',
                            'Arguments': {'#text': '-t1 -t2'},
                            'Config': {'#text': '{{ CONFIG_DIR }}/alpha.xml'},
                            'Host': '{{ host.alpha_name }}',
                            'Order': '1'},
                  'Beta': {'#text': '',
                           'Arguments': {'#text': '-t1'},
                           'Config': {'#text': '{{ CONFIG_DIR }}/beta.xml'},
                           'Host': '{{ host.beta_name }}',
                           'Order': '2'},
                  'Gamma': {'#text': '',
                            'Arguments': {'#text': '-t2'},
                            'Config': {'#text': '{{ CONFIG_DIR }}/gamma.xml'},
                            'Host': '{{ host.gamma_name }}',
                            'Order': '3'}},
     'Name': '{{ env_name }}'}
    

    我试图解决两个问题:

    1. Scenario 字典键中缺少,因为根节点已经是 脚本 标签,我不确定我需要做什么,才能使其成为字典的一部分
    2. 如果我取消评论 <Data Count="58" /> ,我得到以下错误:
    AttributeError: 'NoneType' object has no attribute 'strip'
    

    我不确定我需要实现什么类型的if/else条件,我尝试了类似的方法,但它正在设置所有条件 #text 值到 '' 而不是剥离它们:

    '#text': element.text.strip() if isinstance(
        element.text, ElementTree.Element
    ) else '',
    
    1 回复  |  直到 10 月前
        1
  •  2
  •   Barmar    10 月前

    得到 Scenario 在结果中,使用 tree.tag 作为调用函数时最外层字典中的键。

    要处理没有文本的节点,请添加 #text 在单独的语句中键入字典的键,因此它可以是条件的。

    def format_xml_to_dictionary(element: ElementTree.Element):
        '''
        Format xml to dictionary
    
        :param element: Tree element
        :return: Dictionary formatted result
        '''
        try:
            result = {
                **element.attrib,
                **{i.tag: format_xml_to_dictionary(i) for i in element}
            }
            if element.text:
                result['#text'] = element.text.strip()
            return result
        except ElementTree.ParseError as e:
            raise e
    
    if __name__ == '__main__':
        tree = ElementTree.parse('Scenario.xml').getroot()
        scenario = {
            tree.tag: format_xml_to_dictionary(tree)
        }
        pprint(scenario)