أسهل طريقة لتسلسل كائن فئة بسيط مع SimpleJson؟
-
23-09-2019 - |
سؤال
أحاول إجراء تسلسل قائمة بكائنات 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 dict
S) مع الهيكل التالي:
{ '__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 أيضًا:
آمل أن يرى هذا ، ويمكننا التحدث عن البدء في تنفيذ التعليمات البرمجية الخاصة به في حل مفتوح؟