Płaska realizacja
Możesz użyć czegoś takiego:
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)
a następnie przekonwertować na JSON, używając:
c = YourAlchemyClass()
print json.dumps(c, cls=AlchemyEncoder)
Zignoruje pola, które nie są kodowalne (ustaw je na „Brak”).
Nie rozszerza relacji automatycznie (ponieważ może to prowadzić do odwołań do siebie i zapętlać na zawsze).
Rekurencyjna, nieokrągła implementacja
Jeśli jednak wolisz zapętlać się w nieskończoność, możesz użyć:
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
A następnie koduj obiekty za pomocą:
print json.dumps(e, cls=new_alchemy_encoder(), check_circular=False)
Spowoduje to zakodowanie wszystkich dzieci i wszystkich ich dzieci oraz wszystkich ich dzieci ... Potencjalnie, w zasadzie, możesz zakodować całą bazę danych. Kiedy osiągnie coś, co wcześniej było zakodowane, zakoduje to jako „Brak”.
Rekurencyjna, być może cykliczna, selektywna implementacja
Inną alternatywą, prawdopodobnie lepszą, jest możliwość określenia pól, które chcesz rozwinąć:
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
Możesz teraz zadzwonić za pomocą:
print json.dumps(e, cls=new_alchemy_encoder(False, ['parents']), check_circular=False)
Aby rozszerzyć tylko pola SQLAlchemy, na przykład „rodzice”.