使用simplejson序列化一个简单类对象最简单的方法是什么?

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方法吗?最后,引用另一个对象的字段是否显着复杂化了事情?如果是这样,请重新编写代码,仅在类中使用简单字段(如字符串/浮点数等)

谢谢。


https://dev59.com/VHE95IYBdhLWcg3wlu0g - Esteban Küber
1
可能是重复的问题:Python:如何使类可JSON序列化 - Tobias Kienzler
7个回答

30

我之前使用过这种策略,并且非常满意:将您的自定义对象编码为JSON对象文字(类似于Python中的dict),具有以下结构:

{ '__ClassName__': { ... } }

这本质上是一个只有一个项的dict,其单个键是指定编码对象类型的特殊字符串,其值是实例属性的dict。如果这样说有意义的话。

一个非常简单的编码器和解码器实现(从我实际使用的代码简化而来)如下:

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()类方法,它知道如何从从JSON解码的dict中重新创建一个实例。
很容易扩展编码器和解码器以支持自定义类型(例如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}
        }
    }
}

这与使用类似jsonpickle模块相比如何?谢谢。 - user248237
我刚刚使用新的Underverse模块中的__ClassName__方法编写了非常相似的代码。您还可以通过检查它们是否在TYPES字典中来自动检测要编码的类型,如果没有,您可以添加它们并实时编码。然后,您只需在解码JSON时添加一个类列表,编码就会自动处理。 - max
好的解决方案,但由于你保存类名的方式,它不适用于任何以下划线开头或结尾的类。最好直接使用原始类名。 - codeMonkey

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格式的字符串,用于:

  • 一个自定义类的实例,

  • 一个字典,其叶子节点是自定义类的实例,

  • 一个自定义类实例的列表。

这太棒了。字典案例应该如此处理吗? elif isinstance(obj, dict): objc = obj.copy() for key in obj: objc[key] = serialize(obj[key]) return objc另外,“basestring”在Python3中已经不存在,现在是“str”,而且Python 3不需要“long”。 - Den-Jason

3
根据Python的JSON文档//help(json.dumps)//>中所述,
您应该简单地重写JSONEncoderdefault()方法以提供自定义类型转换,并将其作为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)

2

1
我有一个类似的问题,但是 json.dump 函数不是由我调用的。 因此,为了使 MyClass 可以在 JSON 序列化时无需向 json.dump 提供自定义编码器,您需要对 json 编码器进行 Monkey patch。
首先,在您的模块 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编码器进行monkey patch:

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

现在,即使是在一个外部库中调用json.dump(其中无法更改cls参数)也可以处理你的my_module.MyClass对象。

1
这有点像黑客的做法,我确信其中可能存在很多问题。然而,我正在编写一个简单的脚本,遇到了这样一个问题:我不想为序列化模型对象列表而创建子类的JSON序列化器。我最终使用了列表推导式。
假设: assets = 模型对象列表
代码:
myJson = json.dumps([x.__dict__ for x in assets])

到目前为止,对我的需求似乎非常完美。

0

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

请参见他们网站上的此模型视图示例

如果您没有使用django-rest-framework,这也可以帮助:

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

可能的解决方案1(或方法):{{link2:David Chambers Design提供了一个不错的解决方案}}

我希望David不介意我在此处复制粘贴他的解决方案代码:

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

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)

请注意,这并不是我的解决方案,所有的功劳归属于附带的链接。我只是觉得这个应该放在stackoverflow上。
这个解决方案也可以在上面的答案中实现。
解决方案2:
我更倾向的解决方案可以在这个页面找到:

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

顺便提一下,我看到了这个第二个最佳解决方案的作者:他也在stackoverflow上:

Selaux

我希望他能看到这个,我们可以讨论开始在开放式解决方案中实现和改进他的代码吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接