Python中点表示法转为Json

3 投票
1 回答
9770 浏览
提问于 2025-04-18 17:49

我从Loggly服务那里收到的数据是用点表示法的,但如果要把数据放回去,就必须用JSON格式。

所以,我需要把:

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

转换成:

{'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 个回答

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}

撰写回答