32

我正在尝试使用 JSON(使用 simplejson)序列化 python 对象列表,并且收到对象“不是 JSON 可序列化”的错误。

该类是一个简单的类,其字段只有整数、字符串和浮点数,并从一个父超类继承类似的字段,例如:

class ParentClass:
  def __init__(self, foo):
     self.foo = foo

class ChildClass(ParentClass):
  def __init__(self, foo, bar):
     ParentClass.__init__(self, foo)
     self.bar = bar

bar1 = ChildClass(my_foo, my_bar)
bar2 = ChildClass(my_foo, my_bar)
my_list_of_objects = [bar1, bar2]
simplejson.dump(my_list_of_objects, my_filename)

其中 foo, bar 是我上面提到的简单类型。唯一棘手的事情是 ChildClass 有时有一个引用另一个对象的字段(不是 ParentClass 或 ChildClass 的类型)。

使用 simplejson 将其序列化为 json 对象的最简单方法是什么?使其可序列化为字典就足够了吗?简单地为 ChildClass编写dict方法是最好的方法吗?最后,拥有引用另一个对象的字段是否会使事情变得非常复杂?如果是这样,我可以重写我的代码,只在类中包含简单的字段(如字符串/浮点数等)

谢谢你。

4

7 回答 7

30

我过去使用过这种策略并且对此非常满意:将您的自定义对象编码为 JSON 对象文字(如 Python dicts),具有以下结构:

{ '__ClassName__': { ... } }

这本质上是一个单项dict,它的单个键是一个特殊的字符串,它指定编码哪种对象,其值是dict实例属性的 a。如果这是有道理的。

编码器和解码器的一个非常简单的实现(根据我实际使用的代码进行了简化)如下所示:

TYPES = { 'ParentClass': ParentClass,
          'ChildClass': ChildClass }


class CustomTypeEncoder(json.JSONEncoder):
    """A custom JSONEncoder class that knows how to encode core custom
    objects.

    Custom objects are encoded as JSON object literals (ie, dicts) with
    one key, '__TypeName__' where 'TypeName' is the actual name of the
    type to which the object belongs.  That single key maps to another
    object literal which is just the __dict__ of the object encoded."""

    def default(self, obj):
        if isinstance(obj, TYPES.values()):
            key = '__%s__' % obj.__class__.__name__
            return { key: obj.__dict__ }
        return json.JSONEncoder.default(self, obj)


def CustomTypeDecoder(dct):
    if len(dct) == 1:
        type_name, value = dct.items()[0]
        type_name = type_name.strip('_')
        if type_name in TYPES:
            return TYPES[type_name].from_dict(value)
    return dct

在此实现中,假设您正在编码的对象将具有一个from_dict()类方法,该方法知道如何从dict解码的 JSON 中重新创建一个实例。

datetime扩展编码器和解码器以支持自定义类型(例如对象)很容易。

编辑,回答您的编辑:这样的实现的好处是它会自动编码和解码TYPES映射中找到的任何对象的实例。这意味着它会像这样自动处理 ChildClass:

class ChildClass(object):
    def __init__(self):
        self.foo = 'foo'
        self.bar = 1.1
        self.parent = ParentClass(1)

这应该会导致 JSON 如下所示:

{ '__ChildClass__': {
    'bar': 1.1,
    'foo': 'foo',
    'parent': {
        '__ParentClass__': {
            'foo': 1}
        }
    }
}
于 2010-02-26T17:49:22.463 回答
10

借助以下函数,可以将自定义类的实例表示为 JSON 格式的字符串:

def json_repr(obj):
  """Represent instance of a class as JSON.
  Arguments:
  obj -- any object
  Return:
  String that reprent JSON-encoded object.
  """
  def serialize(obj):
    """Recursively walk object's hierarchy."""
    if isinstance(obj, (bool, int, long, float, basestring)):
      return obj
    elif isinstance(obj, dict):
      obj = obj.copy()
      for key in obj:
        obj[key] = serialize(obj[key])
      return obj
    elif isinstance(obj, list):
      return [serialize(item) for item in obj]
    elif isinstance(obj, tuple):
      return tuple(serialize([item for item in obj]))
    elif hasattr(obj, '__dict__'):
      return serialize(obj.__dict__)
    else:
      return repr(obj) # Don't know how to handle, convert to string
  return json.dumps(serialize(obj))

此函数将生成 JSON 格式的字符串

  • 自定义类的实例,

  • 具有自定义类实例作为叶子的字典,

  • 自定义类的实例列表
于 2011-01-13T16:38:01.953 回答
2

如果您使用的是 Django,则可以通过 Django 的序列化程序模块轻松完成。更多信息可以在这里找到:https ://docs.djangoproject.com/en/dev/topics/serialization/

于 2011-08-01T00:39:40.743 回答
2

在 python 的 JSON 文档中指定 // help(json.dumps)// >

您应该简单地重写default()方法JSONEncoder以提供自定义类型转换,并将其作为cls参数传递。

这是我用来介绍 Mongo 的特殊数据类型(datetime 和 ObjectId)的一种

class MongoEncoder(json.JSONEncoder):
    def default(self, v):
        types = {
            'ObjectId': lambda v: str(v),
            'datetime': lambda v: v.isoformat()
        }
        vtype = type(v).__name__
        if vtype in types:
            return types[type(v).__name__](v)
        else:
            return json.JSONEncoder.default(self, v)     

调用它很简单

data = json.dumps(data, cls=MongoEncoder)
于 2013-06-12T01:55:58.910 回答
1

这有点骇人听闻,我敢肯定它可能有很多问题。但是,我正在生成一个简单的脚本,并且我运行了我不想将我的 json 序列化程序子类化来序列化模型对象列表的问题。我最终使用了列表理解

让:资产=模型对象列表

代码:

myJson = json.dumps([x.__dict__ for x in assets])

到目前为止,似乎对我的需求很有帮助

于 2014-06-11T19:33:53.563 回答
1

我有类似的问题,但json.dump我没有调用该函数。因此,要使MyClassJSON 可序列化而不给您自定义编码器,json.dump您必须猴子修补 json 编码器。

首先在您的模块中创建您的编码器my_module

import json

class JSONEncoder(json.JSONEncoder):
    """To make MyClass JSON serializable you have to Monkey patch the json
    encoder with the following code:
    >>> import json
    >>> import my_module
    >>> json.JSONEncoder.default = my_module.JSONEncoder.default
    """
    def default(self, o):
        """For JSON serialization."""
        if isinstance(o, MyClass):
            return o.__repr__()
        else:
            return super(self,o)

class MyClass:
    def __repr__(self):
        return "my class representation"

然后正如评论中所述,猴子修补 json 编码器:

import json
import my_module
json.JSONEncoder.default = my_module.JSONEncoder.default

现在,即使是json.dump在外部库(您无法更改cls参数)中的调用也适用于您的my_module.MyClass对象。

于 2018-04-25T14:44:43.940 回答
0

现在重读我可能的 2 个解决方案,我觉得有点傻,当然,当您使用 django-rest-framework 时,该框架为上述问题构建了一些出色的功能。

在他们的网站上查看这个模型视图示例

如果您不使用 django-rest-framework,这无论如何都会有所帮助:

我在此页面中找到了解决此问题的 2 个有用的解决方案:(我最喜欢第二个!)

可能的解决方案 1(或可行的方法): David Chambers Design 提出了一个不错的解决方案

我希望大卫不介意我在这里复制粘贴他的解决方案代码:

在实例的模型上定义一个序列化方法:

def toJSON(self):
import simplejson
return simplejson.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))

他甚至提取了上面的方法,所以它更具可读性:

def toJSON(self):
fields = []
for field in self._meta.fields:
    fields.append(field.name)

d = {}
for attr in fields:
    d[attr] = getattr(self, attr)

import simplejson
return simplejson.dumps(d)

请注意,这不是我的解决方案,所有学分都包含在链接中。只是认为这应该是堆栈溢出。

这也可以在上面的答案中实现。

解决方案2:

在此页面上可以找到我更喜欢的解决方案:

http://www.traddicts.org/webdevelopment/flexible-and-simple-json-serialization-for-django/

顺便说一句,我看到了第二个也是最好的解决方案的作者:也在 stackoverflow 上:

塞劳

我希望他能看到这一点,我们可以谈谈在一个开放的解决方案中开始实施和改进他的代码吗?

于 2012-08-15T19:54:05.573 回答