【问题标题】:Parsing an xml file with an ordered dictionary使用有序字典解析 xml 文件
【发布时间】:2025-11-29 00:05:02
【问题描述】:

我有一个xml 格式的文件:

<NewDataSet>
    <Root>
        <Phonemic>and</Phonemic>
        <Phonetic>nd</Phonetic>
        <Description/>
        <Start>0</Start>
        <End>8262</End>
    </Root>
    <Root>
        <Phonemic>comfortable</Phonemic>
        <Phonetic>comfetebl</Phonetic>
        <Description>adj</Description>
        <Start>61404</Start>
        <End>72624</End>
    </Root>
</NewDataSet>

我需要处理它,例如,当用户输入nd 时,程序将其与&lt;Phonetic&gt; 标记匹配,并从&lt;Phonemic&gt; 部分返回and。我想也许如果我可以将 xml 文件转换为字典,我将能够遍历数据并在需要时查找信息。

我搜索并找到了用于相同目的的xmltodict

import xmltodict
with open(r'path\to\1.xml', encoding='utf-8', errors='ignore') as fd:
    obj = xmltodict.parse(fd.read())

运行它会给我一个ordered dict

>>> obj
OrderedDict([('NewDataSet', OrderedDict([('Root', [OrderedDict([('Phonemic', 'and'), ('Phonetic', 'nd'), ('Description', None), ('Start', '0'), ('End', '8262')]), OrderedDict([('Phonemic', 'comfortable'), ('Phonetic', 'comfetebl'), ('Description', 'adj'), ('Start', '61404'), ('End', '72624')])])]))])

不幸的是,这并没有让事情变得更简单,我不确定如何使用新的数据结构来实现程序。例如要访问nd,我必须写:

obj['NewDataSet']['Root'][0]['Phonetic']

这非常复杂。我试图通过dict() 将它变成一个常规字典,但是由于它是嵌套的,所以内层保持有序并且我的数据很大。

【问题讨论】:

  • 转换成普通字典会有什么不同?您仍将拥有尽可能多的密钥层。 究竟是什么问题;你只是不喜欢OrderedDict.__repr__吗?

标签: python xml dictionary xml-parsing xmltodict


【解决方案1】:

您实际上可以通过设置额外的关键字参数来避免转换为 OrderedDict:

obj = xmltodict.parse(xmldata, dict_constructor=dict)

parse 将关键字参数转发给_DictSAXHandlerdict_constructor 默认设置为OrderedDict

【讨论】:

    【解决方案2】:

    Mu 的回答对我有用,我唯一需要改变的是棘手的 ensure root_element 始终是一个列表 步骤。:-

    import xmltodict
    from collections import OrderedDict
    
    xmldata = """<NewDataSet>
        <Root>
            <Phonemic>and</Phonemic>
            <Phonetic>nd</Phonetic>
            <Description/>
            <Start>0</Start>
            <End>8262</End>
        </Root>
        <Root>
            <Phonemic>comfortable</Phonemic>
            <Phonetic>comfetebl</Phonetic>
            <Description>adj</Description>
            <Start>61404</Start>
            <End>72624</End>
        </Root>
    </NewDataSet>"""
    
    obj = xmltodict.parse(xmldata)
    obj = obj["NewDataSet"]
    root_elements = obj["Root"] if type(obj["Root"]) == list else [obj["Root"]] 
    # Above step ensures that root_elements is always a list
    # Is obj["Root"] a list already, then use obj["Root"], otherwise make single element list.
    for element in root_elements:
        print element["Phonetic"]
    

    【讨论】:

      【解决方案3】:

      如果您以 obj['NewDataSet']['Root'][0]['Phonetic'] 的身份访问此内容,IMO,那么您的做法不对。

      相反,您可以执行以下操作

      obj = obj["NewDataSet"]
      root_elements = obj["Root"] if type(obj) == OrderedDict else [obj["Root"]] 
      # Above step ensures that root_elements is always a list
      for element in root_elements:
          print element["Phonetic"]
      

      尽管这段代码看起来要长得多,但优点是一旦开始处理足够大的 xml,它就会更加紧凑和模块化。

      PS:xmltodict 我也遇到了同样的问题。但不是使用 xml.etree.ElementTree 解析 xml 文件,xmltodict 更容易使用,因为代码库更小,而且我不必处理 xml 模块的其他空洞。

      编辑

      以下代码对我有用

      import xmltodict
      from collections import OrderedDict
      
      xmldata = """<NewDataSet>
          <Root>
              <Phonemic>and</Phonemic>
              <Phonetic>nd</Phonetic>
              <Description/>
              <Start>0</Start>
              <End>8262</End>
          </Root>
          <Root>
              <Phonemic>comfortable</Phonemic>
              <Phonetic>comfetebl</Phonetic>
              <Description>adj</Description>
              <Start>61404</Start>
              <End>72624</End>
          </Root>
      </NewDataSet>"""
      
      obj = xmltodict.parse(xmldata)
      obj = obj["NewDataSet"]
      root_elements = obj["Root"] if type(obj) == OrderedDict else [obj["Root"]] 
      # Above step ensures that root_elements is always a list
      for element in root_elements:
          print element["Phonetic"]
      

      【讨论】:

      • 谢谢。我认为最后一行应该是print element[0]['Phonemic'],否则它会抱怨索引应该是整数而不是str
      • @novice66 不,不会,因为我使用了 for 循环,所以索引被处理了。您在尝试代码时遇到任何问题吗?
      • 我刚刚运行它(在 Python 3 中,在 print 周围添加了括号),我得到了错误:TypeError: list indices must be integers, not str
      • @novice66 检查编辑。我在 python 2 上,所以这可能是导致它的原因。
      • 在您最后的陈述中,与etree 相比,您的意思是xmltodict 的代码更精简,因此与其他更复杂的代码相比更易于处理?