Вложенное значение словаря из ключевого пути

Получить значение из вложенного словаря с помощью пути ключа, вот dict:

json = {
    "app": {
        "Garden": {
            "Flowers": {
                "Red flower": "Rose",
                "White Flower": "Jasmine",
                "Yellow Flower": "Marigold"
            }
        },
        "Fruits": {
            "Yellow fruit": "Mango",
            "Green fruit": "Guava",
            "White Flower": "groovy"
        },
        "Trees": {
            "label": {
                "Yellow fruit": "Pumpkin",
                "White Flower": "Bogan"
            }
        }
    }

Входным параметром метода является ключевой путь с выделенными точками, из ключевого пути = "app.Garden.Flowers.white Flower" нужно напечатать "Жасмин". Мой код:

import json
with open('data.json') as data_file:    
  j = json.load(data_file)


def find(element, JSON):     
  paths = element.split(".")  
  # print JSON[paths[0]][paths[1]][paths[2]][paths[3]]
  for i in range(0,len(paths)):
    data = JSON[paths[i]]
    # data = data[paths[i+1]]
    print data



find('app.Garden.Flowers.White Flower',j)

Ответ 1

Очень близко. Вы должны (как и в своем комментарии) рекурсивно пройти через главный объект JSON. Вы можете выполнить это, сохранив результат самого внешнего ключа/значения, затем используя это, чтобы получить следующий ключ/значение и т.д., Пока вы не пройдете путь.

def find(element, JSON):     
  paths = element.split(".")
  data = JSON
  for i in range(0,len(paths)):
    data = data[paths[i]]
  print data

Однако вам все равно нужно следить за KeyErrors.

Ответ 2

Это пример сгиба. Вы можете написать это кратко, как это:

import operator

def find(element, json):
    return reduce(operator.getitem, element.split('.'), json)

Или более Pythonically (потому что reduce() хмурится из-за плохой читаемости), как это:

def find(element, json):
    keys = element.split('.')
    rv = json
    for key in keys:
        rv = rv[key]
    return rv

j = {"app": {
    "Garden": {
        "Flowers": {
            "Red flower": "Rose",
            "White Flower": "Jasmine",
            "Yellow Flower": "Marigold"
        }
    },
    "Fruits": {
        "Yellow fruit": "Mango",
        "Green fruit": "Guava",
        "White Flower": "groovy"
    },
    "Trees": {
        "label": {
            "Yellow fruit": "Pumpkin",
            "White Flower": "Bogan"
        }
    }
}}
print find('app.Garden.Flowers.White Flower', j)

Ответ 3

Немного поздно для вечеринки, но я был в аналогичной ситуации и нашел этот dpath module. Приятно и легко.

Надеюсь, это поможет кому-то еще:)

Ответ 4

Ваш код сильно зависит от того, нет ли точек, которые встречаются в именах ключей, которые вы могли бы контролировать, но не обязательно.

Я бы пошел на общее решение, используя список имен элементов, а затем сгенерировал список, например. разделяя пунктирный список имен клавиш:

class ExtendedDict(dict):
    """changes a normal dict into one where you can hand a list
    as first argument to .get() and it will do a recursive lookup
    result = x.get(['a', 'b', 'c'], default_val)
    """
    def multi_level_get(self, key, default=None):
        if not isinstance(key, list):
            return self.get(key, default)
        # assume that the key is a list of recursively accessible dicts
        def get_one_level(key_list, level, d):
            if level >= len(key_list):
                if level > len(key_list):
                    raise IndexError
                return d[key_list[level-1]]
            return get_one_level(key_list, level+1, d[key_list[level-1]])

        try:
            return get_one_level(key, 1, self)
        except KeyError:
            return default

    get = multi_level_get # if you delete this, you can still use the multi_level-get

Как только у вас есть этот класс, легко просто преобразовать свой dict и получить "Жасмин":

json = {
        "app": {
            "Garden": {
                "Flowers": {
                    "Red flower": "Rose",
                    "White Flower": "Jasmine",
                    "Yellow Flower": "Marigold"
                }
            },
            "Fruits": {
                "Yellow fruit": "Mango",
                "Green fruit": "Guava",
                "White Flower": "groovy"
            },
            "Trees": {
                "label": {
                    "Yellow fruit": "Pumpkin",
                    "White Flower": "Bogan"
                }
            }
        }
    }

j = ExtendedDict(json)
print j.get('app.Garden.Flowers.White Flower'.split('.'))

вы получите:

Jasmine

Как и обычный get() из dict, вы получаете None, если указанный вами ключ (список) не существует нигде в дереве, и вы можете указать второй параметр как возвращаемое значение вместо None

Ответ 5

Я предлагаю вам использовать python-benedict, подкласс python dict с полной поддержкой keypath и многими вспомогательными методами.

Вам просто нужно разыграть существующий дикт:

d = benedict(json)
# now your keys support dotted keypaths
print(d['app.Garden.Flower.White Flower'])

Вот библиотека и документация: https://github.com/fabiocaccamo/python-benedict