5

我正在寻找一个函数,它以 JSON 格式在 python 任意嵌套的 dict/array 上运行,并返回一个字符串列表,该列表键入它包含的所有变量名,深度无限。所以,如果对象是...

x = {
    'a': 'meow',
    'b': {
        'c': 'asd'
    },
    'd': [
        {
            "e": "stuff",
            "f": 1
        },
        {
            "e": "more stuff",
            "f": 2
        }
    ]
}

mylist = f(x)会回来...

>>> mylist
['a', 'b', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']
4

4 回答 4

6
def dot_notation(obj, prefix=''):
     if isinstance(obj, dict):
         if prefix: prefix += '.'
         for k, v in obj.items():
             for res in dot_notation(v, prefix+str(k)):
                 yield res
     elif isinstance(obj, list):
         for i, v in enumerate(obj):
             for res in dot_notation(v, prefix+'['+str(i)+']'):
                 yield res
     else:
         yield prefix

例子:

>>> list(dot_notation(x))
['a', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']
于 2013-07-30T17:15:37.533 回答
3

这是一个有趣的。我使用递归解决了它。

def parse(d):
    return parse_dict(d)

def parse_dict(d):
    items = []
    for key, val in d.iteritems():
        if isinstance(val, dict):
            # use dot notation for dicts
            items += ['{}.{}'.format(key, vals) for vals in parse_dict(val)]
        elif isinstance(val, list):
            # use bracket notation for lists
            items += ['{}{}'.format(key, vals) for vals in parse_list(val)]
        else:
            # just use the key for everything else
            items.append(key)
    return items

def parse_list(l):
    items = []
    for idx, val in enumerate(l):
        if isinstance(val, dict):
            items += ['[{}].{}'.format(idx, vals) for vals in parse_dict(val)]
        elif isinstance(val, list):
            items += ['[{}]{}'.format(idx, vals) for vals in parse_list(val)]
        else:
            items.append('[{}]'.format(val))
    return items

这是我的结果:

>>> parse(x)
['a', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']

编辑

这里再次使用生成器,因为我喜欢 Fj 的回答

def parse(d):
    return list(parse_dict(d))

def parse_dict(d):
    for key, val in d.iteritems():
        if isinstance(val, dict):
            # use dot notation for dicts
            for item in parse_dict(val):
                yield '{}.{}'.format(key, item)
        elif isinstance(val, list):
            # use bracket notation
            for item in parse_list(val):
                yield '{}{}'.format(key, item)
        else:
            # lowest level - just use the key
            yield key

def parse_list(l):
    for idx, val in enumerate(l):
        if isinstance(val, dict):
            for item in parse_dict(val):
                yield '[{}].{}'.format(idx, item)
        elif isinstance(val, list):
            for item in parse_list(val):
                yield '[{}]{}'.format(idx, item)
        else:
            yield '[{}]'.format(val)

同样的结果:

>>> parse(x)
['a', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']
于 2013-07-30T17:26:57.037 回答
2

如果您的对象的顶层可以是一个列表(数组,在 JSON 术语中),那么您的输出格式将不起作用:例如["foo", "bar"],您会在逻辑上返回['[0]', '[1]'],这可能不是您想要的。您还可以通过将对象名称传递给 FJ 的答案稍作修改来解决此问题:

def paths(container, name):
    if isinstance(container, list):
        for i, element in enumerate(container):
            for path in paths(element, "%s[%d]" % (name, i)):
                yield path
    elif isinstance(container, dict):
        for k, element in container.items():
            for path in paths(element, "%s.%s" % (name, k)):
                yield path
    else:
        yield name

用法:

>>> list(paths(x, "x"))
['x.a', 'x.b.c', 'x.d[0].e', 'x.d[0].f', 'x.d[1].e', 'x.d[1].f']
>>> list(paths(["foo", "bar"], "array"))
['array[0]', 'array[1]']

Python 3.3 引入了一种yield from语法,使它更简洁:

def paths(container, name):
    if isinstance(container, list):
        for i, element in enumerate(container):
            yield from paths(element, "%s[%d]" % (name, i))
    elif isinstance(container, dict):
        for k, element in container.items():
            yield from paths(element, "%s.%s" % (name, k))
    else:
        yield name
于 2013-07-30T17:35:25.457 回答
2

仅限 Python 3.3+:

def f(x, parent=''):
    if isinstance(x, dict):
        if parent:
            parent += '.'
        for key in x:
            yield parent + key
            yield from f(x[key], parent + key)
    elif isinstance(x, list):
        for idx, subx in enumerate(x):
            yield from f(subx, '{}[{}]'.format(parent, idx))

Python 2.x ~ Python 3.2

def f(x, parent=''):
    if isinstance(x, dict):
        if parent:
            parent += '.'
        for key in x:
            yield parent + key
            for y in f(x[key], parent + key): yield y
    elif isinstance(x, list):
        for idx, subx in enumerate(x):
            for y in f(subx, '{}[{}]'.format(parent, idx)): yield y

>>> x = {'a': 'meow', 'b': {'c': 'asd'}, 'd':[{"e":"stuff","f":1}, {"e":"more stuff","f":2}]}
>>> list(f(x))
['d', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f', 'a', 'b', 'b.c']
于 2013-07-30T17:43:11.637 回答