【问题标题】:Json dumping a dict throws TypeError: keys must be a stringJson 转储 dict 会引发 TypeError:键必须是字符串
【发布时间】:2012-09-25 22:00:45
【问题描述】:

我正在尝试使用 json.dumps 将以下 dict 转换为 JSON:

 {
     'post_engaged': 36,
     'post_impressions': 491,
     'post_story': 23,
     'comment_count': 6,
     'created_time': '03:02 AM, Sep 30, 2012',
     'message': 'Specialities of Shaktis and Pandavas. \n While having power, why there isn\\u2019t',
     < built - in function id > : '471662059541196',
     'status_type': 'status',
     'likes_count': 22
 } {
     'post_engaged': 24,
     'text': '30 Sept 2012 Avyakt Murlli ( Dual Voice )',
     'post_story': 8,
     'comment_count': 3,
     'link': 'http:\\/\\/www.youtube.com\\/watch?v=VGmFj8g7JFA&feature=youtube_gdata_player',
     'post_impressions': 307,
     'created_time': '03:04 AM, Sep 30, 2012',
     'message': 'Not available',
     < built - in function id > : '529439300404155',
     'status_type': 'video',
     'likes_count': 7
 } {
     'post_engaged': 37,
     'post_impressions': 447,
     'post_story': 22,
     'comment_count': 4,
     'created_time': '03:11 AM, Sep 30, 2012',
     'message': '30-09-12 \\u092a\\u094d\\u0930\\u093e\\u0924:\\u092e\\u0941\\u0930\\u0932\\u0940 \\u0913\\u0',
     < built - in function id > : '471643246209744',
     'status_type': 'status',
     'likes_count': 20
 } {
     'post_engaged': 36,
     'post_impressions': 423,
     'post_story': 22,
     'comment_count': 0,
     'created_time': '03:04 AM, Sep 29, 2012',
     'message': 'Essence: Sweet children, whenever you have time, earn the true income. Staying i',
     < built - in function id > : '471274672913268',
     'status_type': 'status',
     'likes_count': 20
 } {
     'post_engaged': 16,
     'text': 'Essence Of Murli 29-09-2012',
     'post_story': 5,
     'comment_count': 2,
     'link': 'http:\\/\\/www.youtube.com\\/watch?v=i6OgmbRsJpg&feature=youtube_gdata_player',
     'post_impressions': 291,
     'created_time': '03:04 AM, Sep 29, 2012',
     'message': 'Not available',
     < built - in function id > : '213046588825668',
     'status_type': 'video',
     'likes_count': 5
 }

但它引导我去

TypeError : keys must be a string

错误可能是由于dict包含keys like:

 <built-in function id>: '213046588825668'

有人可以指导我,我应该如何从字典中删除这些元素?

【问题讨论】:

  • 唯一正确的解决方案是修复 REAL 错误 - 即使用 id(内置函数)而不是 "id"(文字字符串)作为键 - 在源(在位置这个字典是在哪里构建的)。

标签: python json simplejson


【解决方案1】:

你可以尝试像这样清理它:

for key in mydict.keys():
  if type(key) is not str:
    try:
      mydict[str(key)] = mydict[key]
    except:
      try:
        mydict[repr(key)] = mydict[key]
      except:
        pass
    del mydict[key]

这将尝试将任何不是字符串的键转换为字符串。任何无法转换为字符串或表示为字符串的键都将被删除。

【讨论】:

  • 这完全不是你的错,但这不会满足提问者真正想要的。
  • 将该函数对象作为字典中的键的唯一方法是键入id(这是一个内置函数)而不是"id"(这是一个字符串文字)某处。与其尝试使用更多代码来解决现有的错误,不如尝试找出构建字典的代码在哪里搞砸了?
  • 虽然被接受和赞成,但这个答案实际上是完全错误的,无论是在设计上还是在实现上。对于阅读此答案的任何人:不要这样做
【解决方案2】:

修改上面接受的答案,我写了一个函数来处理任意深度的字典:

def stringify_keys(d):
    """Convert a dict's keys to strings if they are not."""
    for key in d.keys():

        # check inner dict
        if isinstance(d[key], dict):
            value = stringify_keys(d[key])
        else:
            value = d[key]

        # convert nonstring to string if needed
        if not isinstance(key, str):
            try:
                d[str(key)] = value
            except Exception:
                try:
                    d[repr(key)] = value
                except Exception:
                    raise

            # delete old key
            del d[key]
    return d

【讨论】:

    【解决方案3】:

    我知道这是一个老问题,它已经有一个公认的答案,但很遗憾,这个公认的答案是完全错误的。

    这里真正的问题是生成字典的代码使用内置的id 函数作为键而不是文字字符串"id"。因此,简单、明显且唯一正确的解决方案是从源头修复此错误:检查生成 dict 的代码,并将 id 替换为 "id"

    【讨论】:

    • 可能还值得注意的是 json 模块支持自定义序列化类...
    【解决方案4】:

    也许这会有所帮助:

    your_dict = {("a", "b"):[1,2,3,4]}
    # save
    with open("file.json","w") as f:
        f.write(json.dumps(list(your_dict.items())))
    
    # load
    with open("file.json","r") as f:
        your_dict = dict([tuple((tuple(x[0]), x[1])) for x in json.loads(f.read())])
    

    【讨论】:

      【解决方案5】:

      Nolan conaway 的回答给出了这个结果

      {"b'opening_hours'": {"b'1_from_hour'": 720, "b'1_to_hour'": 1440, “b'1_break_from_hour'”:1440,“b'1_break_to_hour'”:1440, “b'2_from_hour'”:720,“b'2_to_hour'”:1440,“b'2_break_from_hour'”: 1440,“b'2_break_to_hour'”:1440,“b'3_from_hour'”:720, “b'3_to_hour'”:1440,“b'3_break_from_hour'”:1440, “b'3_break_to_hour'”:1440,“b'4_from_hour'”:720,“b'4_to_hour'”: 1440,“b'4_break_from_hour'”:1440,“b'4_break_to_hour'”:1440, “b'5_from_hour'”:720,“b'5_to_hour'”:1440,“b'5_break_from_hour'”: 1440,“b'5_break_to_hour'”:1440,“b'6_from_hour'”:720, “b'6_to_hour'”:1440,“b'6_break_from_hour'”:1440, “b'6_break_to_hour'”:1440,“b'7_from_hour'”:720,“b'7_to_hour'”: 1440,“b'7_break_from_hour'”:1440,“b'7_break_to_hour'”:1440}}

      这个修正版

      import time
      import re
      import json
      from phpserialize import *
      
      
      class Helpers:
         def stringify_keys(self,d):
          """Convert a dict's keys to strings if they are not."""
          for key in d.keys():
              # check inner dict
              if isinstance(d[key], dict):
                  value = Helpers().stringify_keys(d[key])
              else:
                  value = d[key]
              # convert nonstring to string if needed
              if not isinstance(key, str):
                  try:
                      d[key.decode("utf-8")] = value
                  except Exception:
                      try:
                          d[repr(key)] = value
                      except Exception:
                          raise
      
                  # delete old key
                  del d[key]
          return d
      

      会给这个更干净的版本..

      {“opening_hours”:{“1_from_hour”:720,“1_to_hour”:1440, “1_break_from_hour”:1440,“1_break_to_hour”:1440,“2_from_hour”: 720,“2_to_hour”:1440,“2_break_from_hour”:1440,“2_break_to_hour”: 1440,“3_from_hour”:720,“3_to_hour”:1440,“3_break_from_hour”: 1440,“3_break_to_hour”:1440,“4_from_hour”:720,“4_to_hour”:1440, “4_break_from_hour”:1440,“4_break_to_hour”:1440,“5_from_hour”: 720,“5_to_hour”:1440,“5_break_from_hour”:1440,“5_break_to_hour”: 1440,“6_from_hour”:720,“6_to_hour”:1440,“6_break_from_hour”: 1440,“6_break_to_hour”:1440,“7_from_hour”:720,“7_to_hour”:1440, “7_break_from_hour”:1440,“7_break_to_hour”:1440}}

      【讨论】:

      • 你能添加Helpers()的导入吗?
      • 查看修改后的代码,简单来说就是递归函数调用自己。
      【解决方案6】:

      理想情况下,您希望清理数据以符合 JSON 支持的数据类型。

      如果你只是想在序列化时从字典中抑制/或删除这些元素,你可以使用skipkeys argument,描述可以在json.dump section中找到

      如果 skipkeys 为 true(默认值:False),则 dict 不属于 基本类型(str、int、float、bool、None)将被跳过而不是 引发 TypeError。

      json.dumps(obj, skipkeys=True)
      

      此解决方案更简洁,允许标准库为您处理错误的密钥。

      警告:您必须充分了解使用这种极端方法的含义,因为这将导致 JSON 键等不合规数据类型的数据丢失。

      【讨论】:

      • 如果你想写入数据,不要使用skipkeys=True,因为这个标志会忽略非str键
      • 可以理解,OP 特别问-“有人可以指导我,我应该如何从字典中删除这些元素?”,我看不出我的解决方案有什么不正确。这几乎就是公认的答案所做的。我也试过这个` >>> import json >>> d = {4: "s"} >>> json.dumps(d) '{"4": "s"}' `
      【解决方案7】:

      也许这会对下一个人有所帮助:

      strjson = json.dumps(str(dic).replace("'",'"'))
      

      【讨论】:

      • 这相当丑陋,并且还转储了 json-ish 编码数据的 json-encoding,因此 json-encoding 执行一次到多次。
      猜你喜欢
      • 2015-11-13
      • 1970-01-01
      • 2020-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-06
      • 2021-08-28
      相关资源
      最近更新 更多