【问题标题】:How to concatenate keys with nested levels in a json file in Python?如何在 Python 的 json 文件中将键与嵌套级别连接起来?
【发布时间】:2023-03-10 12:50:01
【问题描述】:

我正在解决一个问题,我有一个下面给出的 json 对象文件:

{
    "id": "111",
    "name": {
        "firstname": "Tamara",
        "lastname": "Myers"
    },
    "address": {
        "street": "20722 Coleman Villages,East Rose",
        "zip": "71064-5894"
    }
}

我想把它转换成:

{
    "id": "111",
    "name_firstname": "Tamara",
    "name_lastname": "Myers",
    "address_street": "20722 Coleman Villages,East Rose",
    "address_zip": "71064-5894"
}

我无法做到这一点,因为我们可能在其他 json 对象中有更多字段,这些字段在上面的示例中没有给出。

例如"Job":{"Engineer":"Junior","domain":"civil"}。而且所有json对象的嵌套层次也是不规则的。

【问题讨论】:

  • 您的 address_street 键缺少结尾 "
  • 感谢指出,现已修复。
  • 是否有特殊原因需要展平 JSON?否则,听起来像XY Problem
  • 是的,展平后我想将其更改为以键为列的 pandas 数据框。
  • 我很确定 Pandas 可以读取嵌套的 JSON

标签: python json dictionary web-scraping


【解决方案1】:

在 Python 中,您可以随时创建密钥

dic = {}
dic["key"] = value
dic["key"] = "value"
print dic
{'key': 'value'}

你可以对你的对象做这样的事情:

myNewDic = {}
for key, value in dic.items():
    if isinstance(value,dict):
        for k, v in value.items():
            myNewDic[key+"_"+k] = v
    else:
       myNewDic[key] = value

检查是否有效,我想我正在路上。

愉快的编码

【讨论】:

  • 如果嵌套层数超过 2 层,这将不起作用
  • 您好 Ismael,感谢您的参与。我想您已经假设只有两层嵌套。在某些对象中,我有 7 到 8 级嵌套的键。在那种情况下,我不确定它会起作用。
  • 您好,是的,我假设只有两层嵌套,@math2001 或 2rs2ts 发布的函数可以解决您的问题
【解决方案2】:

您需要一个递归函数:它不关心嵌套级别,不像 whilefor 循环。

(递归函数只是一个调用自身的函数)

这个想法是

  • 创建一个新对象(目标对象)
  • 在递归函数中
    • 循环遍历原始对象的每个键
      • 如果值是dict,再次调用this函数
      • 否则,将格式化的键(带下划线)和值添加到目标对象

def flat_keys(obj, new_obj={}, keys=[]):
    for key, value in obj.items():
        if isinstance(value, dict):
            # call the function again if the value is a dict
            # we go one step deeper: obj[key]
            # give the new_obj (by reference, so each call edit the *same* object)
            # give to used keys: keys + [key]
            flat_keys(obj[key], new_obj, keys + [key])
        else:
            new_obj['_'.join(keys + [key])] = value
    return new_obj

new_obj = flat_keys(json.JSONDecoder().decode("your object"))
print(new_obj)

【讨论】:

  • 您好@math2001,非常感谢您的回复。您的代码帮助我解决了这个问题。
  • 不客气!你有什么困惑吗?如果不是,那你能接受这个答案吗? ?
【解决方案3】:

你需要一个递归函数。但是这个比提供的另一个更简单。它也将基本情况放在首位,这将对堆栈大小有所帮助。不过我不能让它尾递归。

def merge_keys(d):
  to_return = {}
  for key, value in d.items():
    if not isinstance(value, dict):
      to_return[key] = value
    else:
      for merged_key, merged_value in merge_keys(value).items():
        to_return["_".join((key, merged_key))] = merged_value
  return to_return

【讨论】:

  • 你好@2rs2ts,你的代码解决了我的问题。非常感谢您的帮助。
【解决方案4】:

我相信递归生成器会很好:

def nested_to_flat(data):                     
     for k, v in data.items():
         if isinstance(v, dict):
             for x, y in nested_to_flat(v):
                 yield ('%s_%s' % (k, x), y)
             continue
         yield (k, v)

并按如下方式使用:

result = {k: v for k, v in nested_to_flat(data)}

【讨论】:

    【解决方案5】:
    import sys
    sys.path.insert(0, '.')
    from sys import stderr
    
    
    def pare(data, key, is_verbose=False):
    
       parts = key.split('.')
    
       i = 0
       ptr = data
    
       for part in parts:
           if is_verbose is True:
              if i > 0:
                  stderr.write(' -> ')
    
              stderr.write(part)
    
           try:
              if issubclass(ptr.__class__, list) is True:
                  ptr = ptr[int(part)]
              else:
                  ptr = ptr[part]
           except:
              if is_verbose is True:
                  stderr.write("\n")
              raise ValueError("Could not descend to child node: %s" % (part))
           i += 1
        if is_verbose is True:
           stderr.write("\n")
        return ptr
    def path_list(dictionary, path):
    
        key_path_list = []
        if dictionary.__class__.__name__ == 'dict':
            if len(dictionary.keys())>0:
              i = 0
              n = len(dictionary.keys())
              while i< n:
                  new_path  = dictionary.keys()[i]
                  i += 1
                  key_path = path + '.' + new_path
                  key_path_list.append(key_path)
        else:
            pass
    return key_path_list
    
    def rec_data(data, key_path):
        pared = pare(data, key_path)
        value = []
        nd = {}
        if pared.__class__.__name__ == 'dict':
            paths = path_list(pared, key_path)
            for p in paths:
               if p in paths:
                       sl = pare(data, p)
                       nd[p] =sl
                       value.append(sl)
               else:
                  pass
               rec_data(data, p)
        else:
            nd[key_path] = pared
        return nd
    def main():
        json = {
        "id": "111",
        "name": {
           "firstname": "Tamara",
           "lastname": "Myers"
        },
        "address": {
           "street": "20722 Coleman Villages,East Rose",
           "zip": "71064-5894"
         }
         }
    
        dic = {}
        for k,v in json.items():
            dic.update(rec_data(json,k))
        print dic
    if __name__ == "__main__":
    
        main()
    

    【讨论】:

      猜你喜欢
      • 2014-12-13
      • 2020-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-13
      • 2014-08-08
      • 2013-01-27
      • 2016-09-23
      相关资源
      最近更新 更多