首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何JSON序列化集合?

如何JSON序列化集合?
EN

Stack Overflow用户
提问于 2011-11-23 00:38:01
回答 11查看 260.9K关注 0票数 174

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

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

代码语言:javascript
复制
  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

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

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

编辑:

谢谢你的回答!也许我应该说得更准确些。

我利用(并提升了)这里的答案来绕过要翻译的set的限制,但也有一些内部键也是一个问题。

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

这个set有很多不同的类型,哈希基本上是为实体计算一个唯一的id,但在真正的NoSQL精神中,没有确切地说明子对象包含什么。

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

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

EN

回答 11

Stack Overflow用户

回答已采纳

发布于 2011-11-23 00:41:32

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

json module docs所示,这种转换可以由JSONEncoder和JSONDecoder自动完成,但这样做可能会放弃一些其他可能需要的结构(如果将集合转换为列表,则将失去恢复常规列表的能力;如果使用dict.fromkeys(s)将集合转换为字典,则将失去恢复字典的能力)。

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

代码语言:javascript
复制
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

下面是一个示例会话,展示了它可以处理列表、字典和集合:

代码语言:javascript
复制
>>> 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 module。它们每个都支持更大范围的数据类型。

票数 126
EN

Stack Overflow用户

发布于 2011-11-23 00:49:28

您可以创建一个自定义编码器,该编码器在遇到set时返回list。下面是一个例子:

代码语言:javascript
复制
>>> import json
>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5]), cls=SetEncoder)
'[1, 2, 3, 4, 5]'

您也可以通过这种方式检测其他类型。如果您需要保留列表实际上是一个集合,则可以使用自定义编码。像return {'type':'set', 'list':list(obj)}这样的东西可能会起作用。

为了说明嵌套类型,请考虑序列化以下内容:

代码语言:javascript
复制
>>> class Something(object):
...    pass
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)

这会引发以下错误:

代码语言:javascript
复制
TypeError: <__main__.Something object at 0x1691c50> is not JSON serializable

这表明编码器将接受返回的list结果,并递归地调用其子对象上的序列化程序。要为多个类型添加自定义序列化程序,可以执行以下操作:

代码语言:javascript
复制
>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       if isinstance(obj, Something):
...          return 'CustomSomethingRepresentation'
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)
'[1, 2, 3, 4, 5, "CustomSomethingRepresentation"]'
票数 132
EN

Stack Overflow用户

发布于 2020-03-05 19:40:19

您不需要创建自定义编码器类来提供default方法-它可以作为关键字参数传入:

代码语言:javascript
复制
import json

def serialize_sets(obj):
    if isinstance(obj, set):
        return list(obj)

    return obj

json_str = json.dumps(set([1,2,3]), default=serialize_sets)
print(json_str)

在所有支持的Python版本中生成[1, 2, 3]

票数 24
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8230315

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档