Python中的点表示法转换为Json

3

我从Loggly服务接收的数据是点符号表示的,但是如果要将数据放回去,它必须是JSON格式。

因此,我需要进行转换:

{'json.message.status.time':50, 'json.message.code.response':80, 'json.time':100}

Into:

{'message': {'code': {'response': 80}, 'status': {'time': 50}}, 'time': 100}

我已经编写了一个函数来实现此功能,但我想知道是否有更直接和简单的方法来达到相同的结果。
def dot_to_json(a):

    # Create root for JSON tree structure
    resp = {}

    for k,v in a.items():
        # eliminate json. (if metric comes from another type, it will keep its root)
        k = re.sub(r'\bjson.\b','',k)
        if '.' in k:
            # Field has a dot
            r = resp
            s = ''
            k2 =  k.split('.')
            l = len(k2)
            count = 0
            t = {}
            for f in k2:
                count += 1
                if f not in resp.keys():
                    r[f]={}
                r = r[f]
                if count < l:
                    s += "['" + f + "']"
                else:
                    s = "resp%s" % s
                    t = eval(s)
                    # Assign value to the last branch
                    t[f] = v
        else:
            r2 = resp
            if k not in resp.keys():
                r2[k] = {}
            r2[k] = v
    return resp

1
那个 eval "resp%s" 是什么意思? - BrenBarn
1个回答

8

您可以使用以下方式将路径转换为字典访问:

def dot_to_json(a):
    output = {}
    for key, value in a.iteritems():
        path = key.split('.')
        if path[0] == 'json':
            path = path[1:]
        target = reduce(lambda d, k: d.setdefault(k, {}), path[:-1], output)
        target[path[-1]] = value
    return output

这将以路径形式接受密钥,忽略第一个 json 部分。使用 reduce() 函数,可以遍历 path 元素(除了最后一个元素),并获取嵌套字典。
基本上,您从 output 开始,对于 path 中的每个元素,获取该值并将其用作下一次迭代的输入。这里使用 dict.setdefault() 函数,在每次键不存在时默认为新的空字典。对于路径 ['foo', 'bar', 'baz'],这归结为调用 output.setdefault('foo', {}).setdefault('bar', {}).setdefault('baz', {}),只不过更紧凑且支持任意长度的路径。
然后使用最内部的字典来设置具有路径最后一个元素作为键的值。
演示:
>>> def dot_to_json(a):
...     output = {}
...     for key, value in a.iteritems():
...         path = key.split('.')[1:]  # ignore the json. prefix
...         target = reduce(lambda d, k: d.setdefault(k, {}), path[:-1], output)
...         target[path[-1]] = value
...     return output
... 
>>> dot_to_json({'json.message.status.time':50, 'json.message.code.response':80, 'json.time':100}))
{'message': {'status': {'time': 50}, 'code': {'response': 80}}, 'time': 100}

看起来他只想忽略前缀如果它是“json”,而在键以其他内容开头时保留它。 - BrenBarn
感谢您的快速回复! - MauricioRoman
我正在尝试理解您的答案是如何工作的。从O'Reilly的《学习Python》第577页中,我了解到reduce函数可以用等效的方式表达为:def myreduce(function, sequence): tally = sequence[0] for next in sequence[1:]: tally = function(tally, next) return tally然而,当应用于上面的解决方案时,我得到了“myreduce()需要恰好2个参数(给出了3个)”。您有调用reduce函数的等效表达式吗? - MauricioRoman
@user3394978:我添加了一个起始值(output 值);如果存在,则用于 tally,循环是在 for next in sequence: 上进行的,而不是跳过第一个值。 - Martijn Pieters
@user3394978:文档或reduce()函数提供了更好的等效方法。 - Martijn Pieters
我从https://dev59.com/VmIk5IYBdhLWcg3wG6s6#25393471的评论中看到,它确实有3个参数,第三个是可选的初始化器。 - MauricioRoman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接