【问题标题】:Python: Loop through all nested key-value pairs created by xmltodictPython:遍历由 xmltodict 创建的所有嵌套键值对
【发布时间】:2016-04-12 17:17:58
【问题描述】:

根据 xml 文件的布局获取特定值非常简单。 (见:StackOverflow

但是当我不知道 xml 元素时,我不能递归它。 由于 xmltodoc 在 OrderedDicts 中嵌套了 OrderedDicts。这些嵌套的 OrderedDicts 由 Python 典型化为类型:'unicode'。而不是(仍然)作为 OrderedDicts。因此像这样循环,不起作用:

def myprint(d):
    for k, v in d.iteritems():
        if isinstance(v, list):
            myprint(v)
        else:
            print "Key :{0},  Value: {1}".format(k, v)

我基本上想要的是对显示每个键值对的整个 xml 文件进行递归。当一个键的值是另一个键值对列表时,它应该递归到它。

将此 xml 文件作为输入:

<?xml version="1.0" encoding="utf-8"?>
<session id="2934" name="Valves" docVersion="5.0.1">
    <docInfo>
        <field name="Employee" isMandotory="True">Jake Roberts</field>
        <field name="Section" isOpen="True" isMandotory="False">5</field>
        <field name="Location" isOpen="True" isMandotory="False">Munchen</field>
    </docInfo>
</session>

和上面列出的代码一样,会话下的所有数据都作为一个值添加到关键会话中。

示例输出:

Key :session,  Value: OrderedDict([(u'@id', u'2934'), (u'@name', u'Valves'), (u'@docVersion', u'5.0.1'), (u'docInfo', OrderedDict([(u'field', [OrderedDict([(u'@name', u'Employee'), (u'@isMandotory', u'True'), ('#text', u'Jake Roberts')]), OrderedDict([(u'@name', u'Section'), (u'@isOpen', u'True'), (u'@isMandotory', u'False'), ('#text', u'5')]), OrderedDict([(u'@name', u'Location'), (u'@isOpen', u'True'), (u'@isMandotory', u'False'), ('#text', u'Munchen')])])]))])

这显然不是我想要的。

【问题讨论】:

  • 你在 else 语句上的缩进是在 for 循环之后,我很确定这不是你想要的,为什么你不能为 isinstance(v,list) 添加一个案例?跨度>
  • 哦,是的。错字。谢谢。
  • 您是否在尝试解析 xml?或者你要去哪里?
  • 是的,我正在尝试解析 xml。只需要 xml 文件中的所有值(带有相应的键),而不需要逐字命名 xml 元素。

标签: python xml recursion


【解决方案1】:

如果您在数据中遇到一个列表,那么您只需要在列表的每个元素上调用 myprint

def myprint(d):
    if isinstance(d,dict): #check if it's a dict before using .iteritems()
        for k, v in d.iteritems():
            if isinstance(v, (list,dict)): #check for either list or dict
                myprint(v)
            else:
                print "Key :{0},  Value: {1}".format(k, v)
    elif isinstance(d,list): #allow for list input too
        for item in d:
            myprint(item)

然后你会得到类似的输出:

...
Key :@name,  Value: Employee
Key :@isMandotory,  Value: True
Key :#text,  Value: Jake Roberts
Key :@name,  Value: Section
Key :@isOpen,  Value: True
Key :@isMandotory,  Value: False
Key :#text,  Value: 5
...

虽然我不确定这有多大用处,因为您有很多重复键,例如 @name,但我想提供一个我之前创建的函数来遍历嵌套 json 嵌套 @987654326 的数据@s 和lists:

def traverse(obj, prev_path = "obj", path_repr = "{}[{!r}]".format, yield_empty_lists_and_dicts=True):
    it = None
    if isinstance(obj,dict):
        it = obj.items()
    elif isinstance(obj,list):
        it = enumerate(obj)
    if it is None or (yield_empty_lists_and_dicts and len(obj)==0):
        yield prev_path,obj
        return
    for k,v in it:
        for data in traverse(v, path_repr(prev_path,k), path_repr):
            yield data

然后就可以遍历数据了:

for path,value in traverse(doc):
    print("{} = {}".format(path,value))

使用prev_pathpath_repr 的默认值,它会给出如下输出:

obj[u'session'][u'@id'] = 2934
obj[u'session'][u'@name'] = Valves
obj[u'session'][u'@docVersion'] = 5.0.1
obj[u'session'][u'docInfo'][u'field'][0][u'@name'] = Employee
obj[u'session'][u'docInfo'][u'field'][0][u'@isMandotory'] = True
obj[u'session'][u'docInfo'][u'field'][0]['#text'] = Jake Roberts
obj[u'session'][u'docInfo'][u'field'][1][u'@name'] = Section
obj[u'session'][u'docInfo'][u'field'][1][u'@isOpen'] = True
obj[u'session'][u'docInfo'][u'field'][1][u'@isMandotory'] = False
obj[u'session'][u'docInfo'][u'field'][1]['#text'] = 5
obj[u'session'][u'docInfo'][u'field'][2][u'@name'] = Location
obj[u'session'][u'docInfo'][u'field'][2][u'@isOpen'] = True
obj[u'session'][u'docInfo'][u'field'][2][u'@isMandotory'] = False
obj[u'session'][u'docInfo'][u'field'][2]['#text'] = Munchen

虽然您可以为path_repr 编写一个函数来获取prev_path 的值(通过递归调用path_repr 确定)和新键,例如一个函数来获取一个元组并在末尾添加另一个元素意味着我们可以获得一个 (tuple of indices : elem) 格式,非常适合传递给 dict 构造函数

def _tuple_concat(tup, idx):
    return (*tup, idx)   
def flatten_data(obj):
    """converts nested dict and list structure into a flat dictionary with tuple keys
    corresponding to the sequence of indices to reach particular element"""
    return dict(traverse(obj, (), _tuple_concat))

new_data = flatten_data(obj)
import pprint
pprint.pprint(new_data)

它以这种字典格式为您提供数据:

{('session', '@docVersion'): '5.0.1',
 ('session', '@id'): 2934,
 ('session', '@name'): 'Valves',
 ('session', 'docInfo', 'field', 0, '#text'): 'Jake Roberts',
 ('session', 'docInfo', 'field', 0, '@isMandotory'): True,
 ('session', 'docInfo', 'field', 0, '@name'): 'Employee',
 ('session', 'docInfo', 'field', 1, '#text'): 5,
 ('session', 'docInfo', 'field', 1, '@isMandotory'): False,
 ('session', 'docInfo', 'field', 1, '@isOpen'): True,
 ('session', 'docInfo', 'field', 1, '@name'): 'Section',
 ('session', 'docInfo', 'field', 2, '#text'): 'Munchen',
 ('session', 'docInfo', 'field', 2, '@isMandotory'): False,
 ('session', 'docInfo', 'field', 2, '@isOpen'): True,
 ('session', 'docInfo', 'field', 2, '@name'): 'Location'}

我发现这在处理我的 json 数据时特别有用,但我不确定你想用你的 xml 做什么。

【讨论】:

  • 哇。太棒了!奇迹般有效。尤其是遍历功能!非常感谢!
  • 这太酷了。我用它来创建一个 pandas DataFrame,这样我就可以比较 json 和 xml。
  • pd.DataFrame.from_records(data=[tup for tup in traverse(xml_dict, root_name)], columns=['key', 'value'], index='key')
猜你喜欢
  • 1970-01-01
  • 2014-09-23
  • 2018-02-11
  • 2022-11-20
  • 1970-01-01
  • 2019-04-24
  • 1970-01-01
  • 2014-01-29
相关资源
最近更新 更多