#python #python-3.x #functional-programming
Вопрос:
Давайте предположим, что у меня есть следующая структура данных python, где runtimeArgs
содержится строка json, а не диктант python.
[ { "runid":"57d45a60-2b34-11ec-8b92-16c898d5a004", "properties":{ "runtimeArgs":"{"date":"2021_10_11","logical.start.time":"1634026435347"}", "phase-1":"7bc31901-2b34-11ec-9194-42010a0c0054" } }, { "runid":"24f7887e-2b28-11ec-b60c-16c898d5a004", "properties":{ "runtimeArgs":"{"date":"2021_10_11","logical.start.time":"1634021196053"}", "phase-1":"4712bfa1-2b28-11ec-8968-42010a0c005a" } } ] # this is working my_list[0]["properties"]['runtimeArgs'] # this not working my_list[0]["properties"]['runtimeArgs']['date']
С for
циклом, и json.loads()
я мог бы преобразовать это в python dict, но я хочу сделать это более функциональным способом, например: понимание карты или списка и т. Д.
Как это возможно сделать?
Ожидаемый результат:
[ { "runid":"57d45a60-2b34-11ec-8b92-16c898d5a004", "properties":{ "runtimeArgs":{"date":"2021_10_11","logical.start.time":"1634026435347"}, "phase-1":"7bc31901-2b34-11ec-9194-42010a0c0054" } }, { "runid":"24f7887e-2b28-11ec-b60c-16c898d5a004", "properties":{ "runtimeArgs":{"date":"2021_10_11","logical.start.time":"1634021196053"}, "phase-1":"4712bfa1-2b28-11ec-8968-42010a0c005a" } } ] # this should work my_list[0]["properties"]['runtimeArgs']['date']
Обновить:
один из способов, которым я мог бы разобраться в этом самостоятельно (что мне не нравится), заключается в следующем:
[{**x, "properties": { "runtimeArgs" : json.loads(x["properties"]["runtimeArgs"]) }} for x in my_list if x]
Есть ли более приятный способ сделать это?
Ответ №1:
Немного отступив, зачем тебе это нужно? Вот несколько причин, которые я могу придумать, с соответствующими решениями:
потому что я сам хочу реализовать синтаксический анализатор JSON, функционально, для практики
В этом случае вы в основном сами по себе, но итеративный токенизатор-это, вероятно, правильный путь.
потому что что-то такое простое, как получение элемента x из этой строки в формате json, должно быть легким, и мне нужно сделать это только один раз
В этом случае используйте json.loads, но заключите свой доступ в функцию:
def get_json(json, key): return json.loads(json)[key] get_json(l[0]["properties"], "date") # this is a function. I reckon that's functional. Here with a comprehension: {l["runid"]: get_json(x["properties"], "date") for x in l}
Потому что я хочу извлечь данные из структуры, десериализуя их по ходу
Используйте функцию:
def get_parse(thing, key): try: return thing[key] except ValueError: return json.loads(thing)[key] get_parse(get_parse(l[0], "properties"), "date")
Эту функцию можно было бы сделать рекурсивной, если бы вы захотели, возвращая самый внутренний элемент.
Я не знаю, правильно ли эти причины описывают ваши варианты использования, но они могут помочь. Основной подход (дорогой для функционального программирования!) состоит в том, чтобы вложить сложную логику в функцию, а затем, если хотите, использовать ее в своем понимании.
Решение для JIT-анализа
Чисто для удовольствия, потому что это меня раздражало, вот класс JS-анализа, похожий на JIT:
from json import loads class JITParser: def __init__(self, thing): if not hasattr(thing, "__getitem__"): self._thing = loads(thing) else: self._thing = thing def get(self, key): val = self._thing[key] if isinstance(val, dict): return JITParser(val) else: try: return JITParser(loads(val)) except ValueError: return val def __repr__(self): return f"JITParser with {repr(self._thing)}" L = [ { "runid": "57d45a60-2b34-11ec-8b92-16c898d5a004", "properties": { "runtimeArgs": '{"date":"2021_10_11","logical.start.time":"1634026435347"}', "phase-1": "7bc31901-2b34-11ec-9194-42010a0c0054", }, }, { "runid": "24f7887e-2b28-11ec-b60c-16c898d5a004", "properties": { "runtimeArgs": '{"date":"2021_10_11","logical.start.time":"1634021196053"}', "phase-1": "4712bfa1-2b28-11ec-8968-42010a0c005a", }, }, ] j = JITParser(L[0]) j.get("runid") j.get("properties") j.get("properties").get("runtimeArgs") j.get("properties").get("runtimeArgs").get("date")
Этот класс будет анализировать дикты или представления диктов в формате json и возвращать объекты JITParser, обертывая их, пока не попадет в то, что не может быть проанализировано как JSON, и в этом случае он вернет сам объект.
Существует множество возможных улучшений: вы могли бы подумать о:
- подкласс диктует и имеет
[]
доступ - реализовать рекурсивный анализ списков
- обрабатывайте другие типы, например объекты с
.dotaccess
помощью . - и т.д.
но это было забавно смоделировать, и это может вдохновить вас, поэтому я оставлю это здесь. Попробуйте сами: это довольно весело.
Ответ №2:
Для этого конкретного набора данных вы могли бы сделать это:
L = [ { "runid":"57d45a60-2b34-11ec-8b92-16c898d5a004", "properties":{ "runtimeArgs":"{"date":"2021_10_11","logical.start.time":"1634026435347"}", "phase-1":"7bc31901-2b34-11ec-9194-42010a0c0054" } }, { "runid":"24f7887e-2b28-11ec-b60c-16c898d5a004", "properties":{ "runtimeArgs":"{"date":"2021_10_11","logical.start.time":"1634021196053"}", "phase-1":"4712bfa1-2b28-11ec-8968-42010a0c005a" } } ] for d in L: print(eval(d['properties']['runtimeArgs'])['date'])
Комментарии:
1. json.loads, вероятно , безопаснее, чем eval, если только оператор действительно не знает, что данные всегда будут в безопасности…
2. @2e0byo Пожалуйста, обратите внимание на мой комментарий в начале моего ответа — т. Е. Это может не быть общим решением этой проблемы