سؤال

أحاول إجراء تسلسل قائمة بكائنات Python مع JSON (باستخدام Simplejson) وأحصل على خطأ في أن الكائن "ليس قابلة للتسلسل".

الفصل عبارة عن فئة بسيطة لها حقول ليست سوى أعداد صحيحة ، وسلاسل ، وعوامات ، ويرث حقول مماثلة من فئة الوالدين الفائقة ، على سبيل المثال:

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 ، شريط أنواع بسيطة كما ذكرت أعلاه. الشيء الوحيد الصعب هو أن ChildClass لديه في بعض الأحيان حقل يشير إلى كائن آخر (من النوع الذي ليس ParentClass أو ChildClass).

ما هي أسهل طريقة لتسلسل هذا ككائن JSON مع Simplejson؟ هل يكفي جعلها قابلة للتسلسل كقاموس؟ هي أفضل طريقة لكتابة ملف قاموس طريقة للطفل؟ أخيرًا ، هل وجود الحقل الذي يشير إلى كائن آخر يعقد الأشياء بشكل كبير؟ إذا كان الأمر كذلك ، فيمكنني إعادة كتابة الكود الخاص بي للحصول على حقول بسيطة فقط في الفصول (مثل الأوتار/العوامات وما إلى ذلك)

شكرا.

هل كانت مفيدة؟

المحلول

لقد استخدمت هذه الاستراتيجية في الماضي وكنت سعيدًا جدًا بها: قم بتشفير كائناتك المخصصة ككائنات 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() طريقة الفصل التي تعرف كيفية أخذ إعادة إنشاء مثيل من أ dict فك تشفير من JSON.

من السهل توسيع المشفر وفك الترميز لدعم الأنواع المخصصة (على سبيل المثال datetime أشياء).

تعديل, ، للإجابة على تحريرك: الشيء الجميل في التنفيذ مثل هذا هو أنه سيتم تشفيره تلقائيًا وفك تشفير مثيلات أي كائن موجود في TYPES رسم الخرائط. هذا يعني أنه سيتعامل تلقائيًا من التعامل مع eildclass مثل:

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

نصائح أخرى

مثال على أ مخصص يمكن تمثيل الفصل كسلسلة تنسيق 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

  • مثيل لفئة مخصصة ،

  • قاموس يحتوي على مثيلات من الفصول المخصصة مثل الأوراق ،

  • قائمة مثيلات الفصول المخصصة

إذا كنت تستخدم Django ، فيمكن القيام به بسهولة عبر وحدة Serializers Django. مزيد من المعلومات يمكن العثور عليها هنا: https://docs.djangoproject.com/en/dev/topics/serialization/

كما هو محدد في مستندات JSON الخاصة بـ Python // 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)

لدي مشكلة مماثلة ولكن json.dump الوظيفة لا تسمى لي. لذلك ، لجعل MyClass JSON التسلسل دون إعطاء تشفير مخصص ل 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"

ثم كما هو موضح في التعليق ، Monkey تصحيح تشفير JSON:

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

الآن ، حتى دعوة json.dump في مكتبة خارجية (حيث لا يمكنك تغيير cls المعلمة) ستعمل من أجل my_module.MyClass أشياء.

هذا نوع من الاختراق وأنا متأكد من أنه ربما يكون هناك الكثير من الخطأ في ذلك. ومع ذلك ، كنت أنتج نصًا بسيطًا وقمت بتشغيل القضية التي لم أكن أرغب في فئة Sublass Subly Serializer لتسلسل قائمة كائنات النموذج. انتهى بي الأمر باستخدام فهم القائمة

دع: الأصول = قائمة modelObjects

شفرة:

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

يبدو أن حتى الآن عملت بشكل ساحر من أجل احتياجاتي

أشعر بسخافة بعض الشيء تجاه حلوتي 2 المحتملة الآن ، بالطبع عندما تستخدم Django-Rest-Framework ، يحتوي هذا الإطار على بعض الميزات الممتازة لهذه المشكلة المذكورة أعلاه.

يرى مثال عرض النموذج هذا على موقعه على الويب

إذا كنت لا تستخدم Django-Rest-Framework ، فقد يساعد ذلك على أي حال:

لقد وجدت حللين مفيدين لهذه المشكلة في هذه الصفحة: (أحب الحل الثاني أكثر!)

الحل المحتمل 1 (أو الطريق للذهاب):قام ديفيد تشامبرز تصميم حل لطيف

آمل ألا يمانع ديفيد في أنني نسخ لصق رمز الحل الخاص به هنا:

تحديد طريقة التسلسل على نموذج المثيل:

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

آمل أن يرى هذا ، ويمكننا التحدث عن البدء في تنفيذ التعليمات البرمجية الخاصة به في حل مفتوح؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top