Recursive search in json with python

Viewed 316

I have a json like this:

{
  "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": []
                }
              }
            ]
          }
        }
      ]
    }
  ]
}

I'm trying to extract a new json like this one:

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

since otherList can be infinite, I'm trying to use a recursive function. unfortunately, It seems that I don't understand something. This is my basic function to get data:

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

then in main.py I call a for to pass over each result, I put timestamp and text in a list like:

 for el in jsonFile:
    textData = getText(jsonresult)

and after that I should run the recursive for all the elements in otherList but I don't understand how.

3 Answers

with recursion you can do something like this:

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)

result:

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

EDIT:

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)

result:

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

Each list entry (the ones in square brackets) seems to contain:

  • timestamp
  • text
  • otherlist

So you need to modify getText to do two things

  • change the method to retrieve more than a single textData result (e.g. store the results in a list)
  • add code to deal with the otherlist entry, in addition to the timestamp and text entries

Like so:

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>

Next you need to add a method to iterate otherList.

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

So stick that into the getText method earlier:

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

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

And finally fix your main.py:

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

Final result:

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

You can use a recursive generator function:

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)))

Output:

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