32

Pythonオブジェクトのリストを(simplejsonを使用して)JSONでシリアル化しようとしていますが、オブジェクトが「JSONシリアル化可能ではない」というエラーが発生します。

このクラスは、整数、文字列、および浮動小数点数のみのフィールドを持つ単純なクラスであり、1つの親スーパークラスから同様のフィールドを継承します。例:

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

私は過去にこの戦略を使用し、非常に満足しています。カスタムオブジェクトをdict次の構造のJSONオブジェクトリテラル(Pythonなど)としてエンコードします。

{ '__ClassName__': { ... } }

dictこれは基本的に、単一のキーがエンコードされるオブジェクトの種類を指定する特別な文字列であり、その値がdictインスタンスの属性の1つである1つのアイテムです。それが理にかなっている場合。

エンコーダーとデコーダーの非常に単純な実装(実際に使用したコードから簡略化)は次のようになります。

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

この実装では、エンコードするオブジェクトに、JSONからデコードされfrom_dict()たインスタンスからインスタンスを再作成する方法を知っているクラスメソッドがあることを前提としています。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}
        }
    }
}
于 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()メソッドをオーバーライドして、引数として渡す必要があります。JSONEncodercls

これは、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シリアライザーをサブクラス化したくないという問題を実行しました。リスト内包表記を使用することになりました

Let:アセット=モデルオブジェクトのリスト

コード:

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

これまでのところ、私のニーズに魅力的に取り組んできたようです

于 2014-06-11T19:33:53.563 に答える
1

同様の問題がありますが、json.dump関数が呼び出されません。したがって、カスタムエンコーダーを提供せずにMyClassJSONをシリアル化するには、jsonエンコーダーにモンキーパッチを適用する必要があります。json.dump

まず、モジュールにエンコーダーを作成します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変更できない)での呼び出しでも、オブジェクトに対して機能します。clsmy_module.MyClass

于 2018-04-25T14:44:43.940 に答える
0

もちろん、django-rest-frameworkを使用する場合、このフレームワークには、上記のこの問題に組み込まれたいくつかの優れた機能があります。

彼らのウェブサイトでこのモデルビューの例を参照してください

django-rest-frameworkを使用していない場合、これはとにかく役立ちます。

私はこのページでこの問題の2つの有用な解決策を見つけました:(私は2番目のものが最も好きです!)

考えられる解決策1(または進むべき道): DavidChambersDesignは素晴らしい解決策を作りました

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:解決策2:

私の好ましい解決策はこのページにあります:

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

ちなみに、私はこの2番目の最良の解決策の作者を見ました:stackoverflowにもあります:

セロー

彼がこれを見てくれることを願っています。オープンソリューションで彼のコードを実装して改善することについて話し合うことができますか?

于 2012-08-15T19:54:05.573 に答える