Рекурсивный поиск в json с помощью python

#python #recursion

Вопрос:

У меня есть такой json:

 {
  "result": [
    {
      "timestamp": 1234567890,
      "textsList": [
        {
          "text": "some text here 0"
        }
      ],
      "otherList": [
        {
          "type": "Nothing"
        },
        {
          "type": "Recursive",
          "data": {
            "timestamp": 12345678901234,
            "textsList": [
              {
                "text": "some other text 1"
              }
            ],
            "otherList": [
              {
                "type": "Nothing"
              },
              {
                "type": "Recursive",
                "data": {
                  "timestamp": 12345678901234,
                  "textsList": [
                    {
                      "text": "some other text 2"
                    }
                  ],
                  "otherList": []
                }
              }
            ]
          }
        }
      ]
    }
  ]
}
 

Я пытаюсь извлечь новый json, подобный этому:

 [{
   "timestamp": 1234567890,
   "text": "some text here 0"
},
{
   "timestamp": 12345678901234,
   "text": "some other text 1"
},
{
   "timestamp": 1234567890,
   "text": "some other text 2"
}
] 
 

поскольку список других может быть бесконечным, я пытаюсь использовать рекурсивную функцию. к сожалению, мне кажется, что я чего-то не понимаю.
Это моя основная функция для получения данных:

 def getText (element):
    textData = {
        "timestamp": element["timestamp"],
        "text": element["textList"][0]["text"],
    }
    return textData
 

затем в main.py Я вызываю команду «для», чтобы передать каждый результат, я помещаю метку времени и текст в список, например:

  for el in jsonFile:
    textData = getText(jsonresult)
 

и после этого я должен запустить рекурсивный для всех элементов в списке других, но я не понимаю, как это сделать.

Комментарии:

1. Чтобы это была рекурсивная функция, вам нужно было бы иногда включать вызов функции изнутри самой функции.

2. да, я знаю это, действуйте в main.py (внутри for) я помещаю рекурсивный вызов, но я не понимаю, как поместить результаты в массив текстовых данных. Я не сообщал о тестируемой функции

3. В main.py -это не то же самое, что внутри самой функции.

4. ммм, я не понимаю, что ты имеешь в виду

5. Вот однострочная рекурсивная функция, которая создает список с обратным отсчетом от n: def count(n): return [n] count(n-1) if n > 0 else [] Смотрите, как функция включает вызов самой себе?

Ответ №1:

с помощью рекурсии вы можете сделать что-то подобное:

 result = []

def finditem(obj, key):
    if key in obj: result.append({"timestamp": obj[key], "text": obj['textsList'][0]['text']})
    for k, v in obj.items():
        if isinstance(v,dict):
            item = finditem(v, key)
            if item is not None:
                result.append({"timestamp": item,"text":v['textsList'][0]['text']})
        elif isinstance(v,list):
            for i in v:
                item = finditem(i, key)
                if item is not None:
                    result.append({"timestamp": item,"text": i['textsList'][0]['text']})

finditem(data, 'timestamp')
print (result)
 

Результат:

 [{'timestamp': 1234567890, 'text': 'some text here 0'}, {'timestamp': 12345678901234, 'text': 'some other text 1'}, {'timestamp': 12345678901234, 'text': 'some other text 2'}]
 

Редактировать:

 result = []
def finditem(obj, key):
    if key in obj and obj[key]=='Recursive': result.append({"timestamp": obj["data"]["timestamp"],"text": obj["data"]['textsList'][0]['text']})
    for k, v in obj.items():
        if isinstance(v,dict):
            item = finditem(v, key)
            if item == 'Recursive':
                d = v['timestamp']
                result.append({"timestamp": d,"text": v['textsList'][0]['text']})
        elif isinstance(v,list):
            for list_item in v:
                item = finditem(list_item, key)
                if item == 'Recursive':
                    d = list_item['timestamp']
                    result.append({"timestamp": d,"text": list_item['textsList'][0]['text']})

finditem(data,'type')
print (result)
 

Результат:

 [{'timestamp': 12345678901234, 'text': 'some other text 1'}, {'timestamp': 12345678901234, 'text': 'some other text 2'}]
 

Комментарии:

1. вместо проверки метки времени это можно сделать, учитывая, является ли «тип» «Рекурсивным» ? так как иногда метка времени присутствует даже в типе ничего….но для полноты я удалил ее в примере json

2. кроме того, я получаю сообщение об ошибке «список» объект не имеет атрибута «элементы»

3. @alexmark Я отредактировал сообщение, я добавил новый код, который выполняет рекурсивный поиск, учитывая, является ли «тип» «Рекурсивным».

Ответ №2:

Каждая запись в списке (те, что в квадратных скобках), по-видимому, содержит:

  • отметка времени
  • текст
  • другой список

Поэтому вам нужно изменить getText, чтобы сделать две вещи

  • измените метод для получения более одного результата TextData (например, сохраните результаты в списке).
  • добавьте код для работы с записью otherlist в дополнение к метке времени и текстовым записям

Вот так:

 def getText (element, resultList):
    textData = {
        "timestamp": element["timestamp"],
        "text": element["textsList"][0]["text"],
    }
    resultList.append(textData)

    otherList = element["otherList"]
    <insert method call to parse otherList into resultList here>
 

Далее вам нужно добавить метод для повторения списка других.

 def processOtherList(data, resultList):
    for el in data:
        if "type" in el and el["type"] == "Recursive":
            getText(el["data"], resultList)
 

Поэтому вставьте это в метод getText ранее:

 def getText (element, resultList):
    textData = {
        "timestamp": element["timestamp"],
        "text": element["textsList"][0]["text"],
    }
    resultList.append(textData)

    otherList = element["otherList"]
    processOtherList(otherList, resultList)
 

И, наконец, исправьте main.py:

 results = []
for el in jsonFile["result"]:
    getText(el, results)
 

Окончательный результат:

https://www.online-python.com/wm8vziKjHA

Комментарии:

1. а итоговый список? Я должен вызвать resultlist = ParseData(jsonFile) с возвращением в ParseData?

2. кроме того, должно быть что-то вроде: если «тип» не в el или el[«тип»] == «Рекурсивный»

3. Я внес некоторые изменения, проверьте ссылку на конечный результат для полностью работающего образца.

Ответ №3:

Вы можете использовать рекурсивную функцию генератора:

 def get_text(d):
   if isinstance(d, dict):
      if 'timestamp' in d:
         yield {'timestamp':d['timestamp'], 'textsList':''.join(i['text'] for i in d['textsList'])}
      for b in d.values():
         yield from get_text(b)
   elif isinstance(d, list):
      yield from [i for j in d for i in get_text(j)]

data = {'result': [{'timestamp': 1234567890, 'textsList': [{'text': 'some text here 0'}], 'otherList': [{'type': 'Nothing'}, {'type': 'Recursive', 'data': {'timestamp': 12345678901234, 'textsList': [{'text': 'some other text 1'}], 'otherList': [{'type': 'Nothing'}, {'type': 'Recursive', 'data': {'timestamp': 12345678901234, 'textsList': [{'text': 'some other text 2'}], 'otherList': []}}]}}]}]}
print(list(get_text(data)))
 

Выход:

 [{'timestamp': 1234567890, 'textsList': 'some text here 0'}, 
 {'timestamp': 12345678901234, 'textsList': 'some other text 1'}, 
 {'timestamp': 12345678901234, 'textsList': 'some other text 2'}]