从密钥列表中获取JSON对象

时间:2016-02-18 09:45:46

标签: python json dictionary

我有一个动态构建的字典,看起来像这样:

my_dict = {"ministry": {
         "of": {
             "silly": {
                 "walks": {}
                 }
             }
          },
    "foo": "bar",
    "spam": {
        "ministry": {
            "of": "silly"
            }
        }
    }

然后我有一个键列表,它是我想要找到的对象的地址,如下所示:

my_keys = ["ministry", "of", "silly", "walks"]

使用my_keys到达my_dict中的位置的最佳方法是什么。在那里,我还想添加一个新条目。

几天来一直在思考和搜索,但没有找到任何东西,只是认为我能想到的是将它重新制作为XML对象。

我想做点什么

my_dict[my_keys[0]][my_keys[1]][my_keys[2]][my_keys[3]] = {"new":"entry"}

但我永远不知道my_keys列表中有多少项。你们怎么解决这个问题?

编辑1: 基于Pauls回答我做了这个

def get(json_object, path):
    if type(path) == str:
        path = path.split(".")

    if type(path) != list or len(path) == 0:
        return

    key = path.pop(0)

    if len(path) == 0:
        try:
            return json_object[key]
        except KeyError:
            return

    if len(path):
        return get(json_object[key], path)

然后使用如下:

my_found_item = get(my_dict, my_keys)
if my_found_item:
    my_found_item["new"] = "entry"

print(my_dict)

如果my_keys是这样的话,哪个有效:

my_keys = ["ministry", "of", "silly"]

但是如果my_keys像这样一直向下移动它将无法正常工作

my_keys = ["ministry", "of", "silly", "walks"]

my_found_item = get(my_dict, my_keys)
if my_found_item:
    my_found_item["new"] = "entry"

有没有人知道如何解决这个问题?'小'问题

编辑2 这当然是因为我需要检查my_found_item是否不是。此更改修复了它:

if my_found_item is not None:
    my_found_item["new"] = "entry"

用答案here

回答了我自己的问题

4 个答案:

答案 0 :(得分:2)

使用递归。

您想要的功能已写入名为dotty的Javascript库中。现在你不能简单地将JS复制并粘贴到python中,你需要做一些翻译。这里要注意的是技术。

特别是他们编写了一个.get()函数,该函数将深入查看对象,以查找使用包含点的字符串定义的路径,例如ministry.of.silly.walks

实施源代码为here

var get = module.exports.get = function get(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return;
  }

  if (path.length === 0) {
    return object[key];
  }

  if (path.length) {
    return get(object[key], path);
  }
};
  1. 该函数旨在将字符串或数组作为参数(path),并确定它是什么。
  2. 描述字符串ministry.of.silly.walks.上拆分,以获取存储在路径中的数组(即路径现在设置为['ministry','of','silly','walks']
  3. path的第一个元素已移除path.shift()并放入key,用于访问对象的一个​​级别(object[key])。
  4. 如果描述符超出级别(path.length === 0),我们就完成了并找到了元素(object[key])。如果没有,我们传递简化级别对象(object[key])和路径数组的剩余部分以递归方式返回到get(return get(object[key], path))。
  5. 这个递归配方应该适用于任何语言。我想你会发现python有.shift()而你.slice()想要替换python中的复制操作,可能是path = path[:]path = copy.copy(path)。如果要在找到对象中的正确位置后添加新条目,则需要为其添加参数并将其复制到递归调用。

答案 1 :(得分:1)

如果您信任输入,则可以从键构建字符串并使用exec:

my_dict = {"ministry": {
         "of": {
             "silly": {
                 "walks": {}
                 }
             }
          },
    "foo": "bar",
    "spam": {
        "ministry": {
            "of": "silly"
            }
        }
    }

my_keys = ["ministry", "of", "silly", "walks"]


exec """my_dict{} = {{"new":"entry"}}""".format("".join(map("['{}']".format, my_keys)))

演示:

In [47]: my_dict = {"ministry": {
         "of": {
             "silly": {
                 "walks": {}
                 }
             }
          },
    "foo": "bar",
    "spam": {
        "ministry": {
            "of": "silly"
            }
        }
    }

In [48]: my_keys = ["ministry", "of", "silly", "walks"]

In [49]: exec """my_dict{} = {{"new":"entry"}}""".format("".join(map("['{}']".format, my_keys)))

In [50]: my_dict
Out[50]: 
{'foo': 'bar',
 'ministry': {'of': {'silly': {'walks': {'new': 'entry'}}}},
 'spam': {'ministry': {'of': 'silly'}}}

如果您还想检索值:

def pairing(d, new,keylist):
    path = "".join(map("['{}']".format, keylist))
    exec "get_val = d{}".format(path)
    exec "d{path} = {new}".format(new=new,path=path)
    return get_val

演示:

In [58]: my_dict = {"ministry": {
     "of": {
         "silly": {
             "walks": {}
             }
         }
      },
"foo": "bar",
"spam": {
    "ministry": {
        "of": "silly"
        }
    }
}

In [59]: pairing(my_dict, {"new":"pairing"}, my_keys)
Out[59]: {}

In [60]: my_dict
Out[60]: 
{'foo': 'bar',
 'ministry': {'of': {'silly': {'walks': {'new': 'pairing'}}}},
 'spam': {'ministry': {'of': 'silly'}}}

如果你传递了错误的密钥,你会得到一个像你通常的keyError:

n [61]: my_keys = ["ministry", "of", "silly", "foo"]

In [62]: pairing(my_dict, {"new":"pairing"}, my_keys)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
...................
<string> in <module>()

KeyError: 'foo'

如果您愿意,可以像往常一样使用try/exceptdict.get来捕捉任何错误。

答案 2 :(得分:1)

基于Pauls关于名为dotty的Javascript的回答。

my_dict = {"ministry": {
         "of": {
             "silly": {
                 "walks": {}
                 }
             }
          },
    "foo": "bar",
    "spam": {
        "ministry": {
            "of": "silly"
            }
        }
    }

my_keys = ["ministry", "of", "silly"]


def get_json(json_object, path):
    __path = path[:]
    if type(__path) == str:
        __path = __path.split(".")

    if type(path) != list or len(__path) == 0:
        return

    key = __path.pop(0)

    if len(__path) == 0:
        try:
            return json_object[key]
        except KeyError:
            return

    if len(__path):
        return get_json(json_object[key], __path)

    my_found_item = get(my_dict, my_keys)
    if my_found_item is not None:
        my_found_item["new"] = "entry"

    print(my_dict)

答案 3 :(得分:0)

您可以使用jsonpointer访问字典中的元素

from jsonpointer import resolve_pointer

my_pointer = '/' + '/'.join(my_keys)

resolve_pointer(my_dict, my_pointer)['new'] = 'entry'

修改  我刚刚发现jsonpath.rw,看起来比jsonpointer更强大