Implementasi yang datar
Anda dapat menggunakan sesuatu seperti ini:
from sqlalchemy.ext.declarative import DeclarativeMeta
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj.__class__, DeclarativeMeta):
# an SQLAlchemy class
fields = {}
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
data = obj.__getattribute__(field)
try:
json.dumps(data) # this will fail on non-encodable values, like other classes
fields[field] = data
except TypeError:
fields[field] = None
# a json-encodable dict
return fields
return json.JSONEncoder.default(self, obj)
lalu konversi ke JSON menggunakan:
c = YourAlchemyClass()
print json.dumps(c, cls=AlchemyEncoder)
Ini akan mengabaikan bidang yang tidak dapat disandikan (atur ke 'Tidak Ada').
Itu tidak memperluas hubungan secara otomatis (karena ini bisa mengarah pada referensi-sendiri, dan berulang selamanya).
Implementasi rekursif, non-sirkuler
Namun, jika Anda lebih suka mengulang selamanya, Anda dapat menggunakan:
from sqlalchemy.ext.declarative import DeclarativeMeta
def new_alchemy_encoder():
_visited_objs = []
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj.__class__, DeclarativeMeta):
# don't re-visit self
if obj in _visited_objs:
return None
_visited_objs.append(obj)
# an SQLAlchemy class
fields = {}
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
fields[field] = obj.__getattribute__(field)
# a json-encodable dict
return fields
return json.JSONEncoder.default(self, obj)
return AlchemyEncoder
Dan kemudian menyandikan objek menggunakan:
print json.dumps(e, cls=new_alchemy_encoder(), check_circular=False)
Ini akan menyandikan semua anak, dan semua anak mereka, dan semua anak mereka ... Berpotensi menyandikan seluruh basis data Anda, pada dasarnya. Ketika mencapai sesuatu yang disandikan sebelumnya, itu akan menyandikannya sebagai 'Tidak Ada'.
Implementasi rekursif, mungkin melingkar, selektif
Alternatif lain, mungkin lebih baik, adalah untuk dapat menentukan bidang yang ingin Anda kembangkan:
def new_alchemy_encoder(revisit_self = False, fields_to_expand = []):
_visited_objs = []
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj.__class__, DeclarativeMeta):
# don't re-visit self
if revisit_self:
if obj in _visited_objs:
return None
_visited_objs.append(obj)
# go through each field in this SQLalchemy class
fields = {}
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
val = obj.__getattribute__(field)
# is this field another SQLalchemy object, or a list of SQLalchemy objects?
if isinstance(val.__class__, DeclarativeMeta) or (isinstance(val, list) and len(val) > 0 and isinstance(val[0].__class__, DeclarativeMeta)):
# unless we're expanding this field, stop here
if field not in fields_to_expand:
# not expanding this field: set it to None and continue
fields[field] = None
continue
fields[field] = val
# a json-encodable dict
return fields
return json.JSONEncoder.default(self, obj)
return AlchemyEncoder
Anda sekarang dapat menyebutnya dengan:
print json.dumps(e, cls=new_alchemy_encoder(False, ['parents']), check_circular=False)
Untuk hanya memperluas bidang SQLAlchemy yang disebut 'parent', misalnya.