使用 simplejson 序列化简单类对象的最简单方法?

发布于 2024-08-23 02:30:27 字数 785 浏览 4 评论 0原文

我正在尝试使用 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 方法是最好的方法吗?最后,拥有引用另一个对象的字段会使事情变得非常复杂吗?如果是这样,我可以重写我的代码,只在类中包含简单的字段(如字符串/浮点数等),

谢谢。

I'm trying to serialize a list of python objects with JSON (using simplejson) and am getting the error that the object "is not JSON serializable".

The class is a simple class having fields that are only integers, strings, and floats, and inherits similar fields from one parent superclass, e.g.:

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)

where foo, bar are simple types like I mentioned above. The only tricky thing is that ChildClass sometimes has a field that refers to another object (of a type that is not ParentClass or ChildClass).

What is the easiest way to serialize this as a json object with simplejson? Is it sufficient to make it serializable as a dictionary? Is the best way to simply write a dict method for ChildClass? Finally, does having the field that refer to another object significantly complicate things? If so, I can rewrite my code to only have simple fields in classes (like strings/floats etc.)

thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

草莓酥 2024-08-30 02:30:28

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

{ '__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}
        }
    }
}

I've used this strategy in the past and been pretty happy with it: Encode your custom objects as JSON object literals (like Python dicts) with the following structure:

{ '__ClassName__': { ... } }

That's essentially a one-item dict whose single key is a special string that specifies what kind of object is encoded, and whose value is a dict of the instance's attributes. If that makes sense.

A very simple implementation of an encoder and a decoder (simplified from code I've actually used) is like so:

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

In this implementation assumes that the objects you're encoding will have a from_dict() class method that knows how to take recreate an instance from a dict decoded from JSON.

It's easy to expand the encoder and decoder to support custom types (e.g. datetime objects).

EDIT, to answer your edit: The nice thing about an implementation like this is that it will automatically encode and decode instances of any object found in the TYPES mapping. That means that it will automatically handle a ChildClass like so:

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

That should result in JSON something like the following:

{ '__ChildClass__': {
    'bar': 1.1,
    'foo': 'foo',
    'parent': {
        '__ParentClass__': {
            'foo': 1}
        }
    }
}
时光磨忆 2024-08-30 02:30:28

借助以下函数,自定义类的实例可以表示为 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))

函数将为

  • 自定义类的实例

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

  • 自定义实例的列表

An instance of a custom class could be represented as JSON formatted string with help of following function:

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))

This function will produce JSON-formatted string for

  • an instance of a custom class,

  • a dictionary that have instances of
    custom classes as leaves,

  • a list of instances of custom
    classes
浅忆 2024-08-30 02:30:28

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

您只需重写 JSONEncoder 的 default() 方法即可提供自定义类型转换,并将其作为 cls 参数传递。

这是我用来介绍 Mongo 的特殊数据类型(日期时间和 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)

As specified in python's JSON docs // help(json.dumps) // >

You should simply override the default() method of JSONEncoder in order to provide a custom type conversion, and pass it as cls argument.

Here is one I use to cover Mongo's special data types (datetime and 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)     

Calling it as simple as

data = json.dumps(data, cls=MongoEncoder)
爱的那么颓废 2024-08-30 02:30:28

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

If you are using Django, it can be easily done via Django's serializers module. More info can be found here: https://docs.djangoproject.com/en/dev/topics/serialization/

陪你搞怪i 2024-08-30 02:30:28

这是一种黑客行为,我确信它可能有很多错误。但是,我正在生成一个简单的脚本,并且遇到了一个问题,即我不想子类化我的 json 序列化器来序列化模型对象列表。我最终使用列表理解

让:
资产=模型对象列表

代码:

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

到目前为止似乎可以很好地满足我的需求

This is kind of hackish and I'm sure there's probably a lot that can be wrong with it. However, I was producing a simple script and I ran the issue that I did not want to subclass my json serializer to serialize a list of model objects. I ended up using list comprehension

Let:
assets = list of modelobjects

Code:

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

So far seems to have worked charmingly for my needs

书间行客 2024-08-30 02:30:28

我有类似的问题,但我没有调用 json.dump 函数。
因此,要使 MyClass JSON 可序列化而不为 json.dump 提供自定义编码器,您必须对 json 编码器进行 Monkey 修补。

首先在模块 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 对象。

I have a similar problem but the json.dump function is not called by me.
So, to make MyClass JSON serializable without giving a custom encoder to json.dump you have to Monkey patch the json encoder.

First create your encoder in your module 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"

Then as it is described in the comment, monkey patch the json encoder:

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

Now, even an call of json.dump in an external library (where you cannot change the cls parameter) will work for your my_module.MyClass objects.

洛阳烟雨空心柳 2024-08-30 02:30:28

我现在重读它时对我可能的两种解决方案感到有点愚蠢,
当然,当您使用 django-rest-framework 时,该框架针对上述问题内置了一些出色的功能。

请参阅其网站上的 此模型视图示例

如果您不使用 django-休息框架,无论如何这都会有所帮助:

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

可能的解决方案 1(或可行的方法):
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)

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

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

解决方案 2:

我的首选解决方案在此页面上找到:

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

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

Selaux

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

I feel a bit silly about my possible 2 solutions rereading it now,
of course when you use django-rest-framework, this framework have some excellent features buildin for this problem mentioned above.

see this model view example on their website

If you're not using django-rest-framework, this can help anyway:

I found 2 helpfull solutions for this problem in this page: (I like the second one the most!)

Possible solution 1 (or way to go):
David Chambers Design made a nice solution

I hope David does not mind I copy paste his solution code here:

Define a serialization method on the instance's model:

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

and he even extracted the method above, so it's more readable:

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)

Please mind, it's not my solution, all the credits goes to the link included. Just thought this should be on stack overflow.

This could be implemented in the answers above as well.

Solution 2:

My preferable solution is found on this page:

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

By the way, i saw the writer of this second and best solution: is on stackoverflow as well:

Selaux

I hope he sees this, and we can talk about starting to implement and improve his code in an open solution?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文