小编典典

如何 JSON 序列化集合?

all

我有一个 Python set,其中包含对象__hash____eq__方法,以确保集合中不包含重复项。

我需要对这个结果进行 json 编码set,但即使将一个空传递set给该json.dumps方法也会引发一个TypeError.

  File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 178, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable

json.JSONEncoder我知道我可以为具有自定义方法的类创建扩展default,但我什至不确定从哪里开始转换set.
我应该使用默认方法中的值创建一个字典set,然后返回其编码吗?理想情况下,我想让默认方法能够处理原始编码器阻塞的所有数据类型(我使用 Mongo
作为数据源,因此日期似乎也会引发此错误)

任何正确方向的提示将不胜感激。

编辑:

感谢你的回答!也许我应该更准确。

我利用(并赞成)这里的答案来绕过set正在翻译的限制,但也有内部密钥也是一个问题。

中的set对象是转换为 的复杂对象__dict__,但它们本身也可以包含其属性的值,这些值可能不适用于 json 编码器中的基本类型。

这里有很多不同的类型set,散列基本上计算实体的唯一 id,但在 NoSQL 的真正精神中,并不能准确地说明子对象包含什么。

一个对象可能包含 的日期值starts,而另一个对象可能有一些其他模式,其中不包含包含“非原始”对象的键。

这就是为什么我能想到的唯一解决方案是扩展JSONEncoder以替换default打开不同情况的方法 -
但我不确定如何解决这个问题并且文档不明确。在嵌套对象中,从default键返回的值,还是只是查看整个对象的通用包含/丢弃?该方法如何适应嵌套值?我查看了以前的问题,似乎找不到针对特定情况进行编码的最佳方法(不幸的是,这似乎是我在这里需要做的)。


阅读 83

收藏
2022-07-14

共1个答案

小编典典

JSON表示法只有少数原生数据类型(对象、数组、字符串、数字、布尔值和 null),因此在 JSON
中序列化的任何内容都需要表示为这些类型之一。

json 模块 docs所示,这种转换可以由
JSONEncoderJSONDecoder
自动完成,但是您将放弃一些您可能需要的其他结构(如果您将集合转换为列表,那么您将失去恢复常规的能力列表;如果您将集合转换为字典,dict.fromkeys(s)则您将失去恢复字典的能力)。

一个更复杂的解决方案是构建一个可以与其他原生 JSON 类型共存的自定义类型。这使您可以存储嵌套结构,包括列表、集合、字典、小数、日期时间对象等:

from json import dumps, loads, JSONEncoder, JSONDecoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        try:
            return {'_python_object': pickle.dumps(obj).decode('latin-1')}
        except pickle.PickleError:
            return super().default(obj)

def as_python_object(dct):
    if '_python_object' in dct:
        return pickle.loads(dct['_python_object'].encode('latin-1'))
    return dct

这是一个示例会话,显示它可以处理列表、字典和集合:

>>> data = [1,2,3, set(['knights', 'who', 'say', 'ni']), {'key':'value'}, Decimal('3.14')]

>>> j = dumps(data, cls=PythonObjectEncoder)

>>> loads(j, object_hook=as_python_object)
[1, 2, 3, set(['knights', 'say', 'who', 'ni']), {'key': 'value'}, Decimal('3.14')]

或者,使用更通用的序列化技术(例如YAMLTwisted
Jelly

Python 的pickle 模块)可能会很有用。这些都支持更大范围的数据类型。

2022-07-14