Jak zrobić serializowalną klasę JSON
Jak zrobić serializowalną klasę Pythona?
Klasa prosta:
class FileItem:
def __init__(self, fname):
self.fname = fname
Co powinienem zrobić, aby móc uzyskać wynik:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Bez błędu
30 answers
Czy masz pojęcie o oczekiwanej wydajności? Na przykład, czy to wystarczy?
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
W takim przypadku możesz jedynie zadzwonić json.dumps(f.__dict__)
.
Jeśli chcesz bardziej spersonalizowane wyjście, będziesz musiał podklasować JSONEncoder
i wdrożyć własną serializację niestandardową.
Dla trywialnego przykładu, patrz poniżej.
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
Następnie przekazujesz tę klasę do json.dumps()
metoda as cls
kwarg:
json.dumps(cls=MyEncoder)
Jeśli chcesz również dekodować, musisz dostarcz Niestandardowy object_hook
do JSONDecoder
klasy. Na przykład:
>>> def from_json(json_object):
if 'fname' in json_object:
return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-13 22:00:24
Oto proste rozwiązanie dla prostej funkcji:
.toJSON()
metoda
Zamiast klasy serializowalnej JSON, zaimplementuj metodę serializera:
import json
class Object:
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
Więc po prostu nazwij to serialize:
me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"
print(me.toJSON())
Wyświetli:
{
"age": 35,
"dog": {
"name": "Apollo"
},
"name": "Onur"
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55
Dla bardziej złożonych klas można rozważyć Narzędzie jsonpickle:
Jsonpickle jest biblioteką Pythona do serializacji i deserializacji złożonych obiektów Pythona do i z JSON.
Standardowe biblioteki Pythona do kodowania Pythona w JSON, takie jak stdlib ' s json, simplejson i demjson, mogą obsługiwać tylko prymitywy Pythona, które mają bezpośredni odpowiednik JSON (np. dicts, lists, strings, ints, itp.). jsonpickle opiera się na tych bibliotekach i umożliwia serializację bardziej złożonych struktur danych do JSON. jsonpickle jest wysoce konfigurowalny i rozszerzalny-pozwalając użytkownikowi wybrać zaplecze JSON i dodać dodatkowe zaplecze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55
Większość odpowiedzi wymaga zmiany połączenia na json.dumps () , co nie zawsze jest możliwe lub pożądane(może się zdarzyć na przykład wewnątrz komponentu frameworka).
Jeśli chcesz być w stanie zadzwonić json.dumps (obj) tak jak jest, wtedy proste rozwiązanie dziedziczy się z dict :
class FileItem(dict):
def __init__(self, fname):
dict.__init__(self, fname=fname)
f = FileItem('tasks.txt')
json.dumps(f) #No need to change anything here
To działa, jeśli twoja klasa jest tylko podstawową reprezentacją danych, dla trudniejszych rzeczy zawsze możesz ustawić klucze jawnie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 13:22:25
Lubię odpowiedź Onura , ale rozszerzyłbym o opcjonalną metodę toJSON()
, aby obiekty mogły się serializować:
def dumper(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45
Inną opcją jest zawinięcie JSON dumping do własnej klasy:
import json
class FileItem:
def __init__(self, fname):
self.fname = fname
def __repr__(self):
return json.dumps(self.__dict__)
Lub, jeszcze lepiej, podklasowanie klasy FileItem z klasy JsonSerializable
:
import json
class JsonSerializable(object):
def toJson(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.toJson()
class FileItem(JsonSerializable):
def __init__(self, fname):
self.fname = fname
Testowanie:
>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-02 17:59:56
Po prostu dodaj to_json
metodę do swojej klasy w następujący sposób:
def to_json(self):
return self.message # or how you want it to be serialized
I dodaj ten kod (z tej odpowiedzi), gdzieś na szczycie wszystkiego:
from json import JSONEncoder
def _default(self, obj):
return getattr(obj.__class__, "to_json", _default.default)(obj)
_default.default = JSONEncoder().default
JSONEncoder.default = _default
To po zaimportowaniu poprawi moduł json tak JSONEncoder.default () automatycznie sprawdza dla specjalnego " to_json()" metoda i używa jej do zakodowania obiektu, jeśli zostanie znaleziony.
Tak jak powiedział Onur, ale tym razem nie musisz aktualizować każdego json.dumps()
w swoim projekcie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-27 02:13:38
Jeśli używasz Python3. 5+, możesz użyć jsons
. Konwertuje Twój obiekt (i wszystkie jego atrybuty rekurencyjnie) na dict.
import jsons
a_dict = jsons.dump(your_object)
Lub jeśli chcesz ciąg:
a_str = jsons.dumps(your_object)
Lub jeśli twoja klasa zaimplementowała jsons.JsonSerializable
:
a_dict = your_object.json
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-19 09:34:30
Natknąłem się na ten problem pewnego dnia i zaimplementowałem bardziej ogólną wersję kodera dla obiektów Pythona, który możeobsługiwać zagnieżdżone obiekty idziedziczyć pola:
import json
import inspect
class ObjectEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, "to_json"):
return self.default(obj.to_json())
elif hasattr(obj, "__dict__"):
d = dict(
(key, value)
for key, value in inspect.getmembers(obj)
if not key.startswith("__")
and not inspect.isabstract(value)
and not inspect.isbuiltin(value)
and not inspect.isfunction(value)
and not inspect.isgenerator(value)
and not inspect.isgeneratorfunction(value)
and not inspect.ismethod(value)
and not inspect.ismethoddescriptor(value)
and not inspect.isroutine(value)
)
return self.default(d)
return obj
Przykład:
class C(object):
c = "NO"
def to_json(self):
return {"c": "YES"}
class B(object):
b = "B"
i = "I"
def __init__(self, y):
self.y = y
def f(self):
print "f"
class A(B):
a = "A"
def __init__(self):
self.b = [{"ab": B("y")}]
self.c = C()
print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)
Wynik:
{
"a": "A",
"b": [
{
"ab": {
"b": "B",
"i": "I",
"y": "y"
}
}
],
"c": {
"c": "YES"
},
"i": "I"
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 14:10:51
import simplejson
class User(object):
def __init__(self, name, mail):
self.name = name
self.mail = mail
def _asdict(self):
return self.__dict__
print(simplejson.dumps(User('alice', '[email protected]')))
Jeśli używasz standardu json
, musisz zdefiniować default
funkcję
import json
def default(o):
return o._asdict()
print(json.dumps(User('alice', '[email protected]'), default=default))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-22 03:08:53
Jak wspomniano w wielu innych odpowiedziach, możesz przekazać funkcję json.dumps
, Aby przekonwertować obiekty, które nie są jednym z obsługiwanych typów domyślnie na Obsługiwany typ. Zaskakująco żaden z nich nie wspomina o najprostszym przypadku, jakim jest użycie wbudowanej funkcji vars
aby przekształcić obiekty w dict zawierający wszystkie ich atrybuty:
json.dumps(obj, default=vars)
Jeśli potrzebujesz bardziej szczegółowej serializacji dla określonych typów (np. z wyłączeniem pewnych atrybutów), możesz użyć funkcji niestandardowej lub JSONEncoder
jako opisane w innych odpowiedziach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-22 10:05:37
json
jest ograniczona pod względem obiektów, które może wydrukować, a jsonpickle
(może być potrzebne pip install jsonpickle
) jest ograniczona pod względem tego, że nie może wcięć tekstu. Jeśli chcesz sprawdzić zawartość obiektu, którego klasy nie możesz zmienić, nadal nie mogę znaleźć prostszego sposobu niż:
import json
import jsonpickle
...
print json.dumps(json.loads(jsonpickle.encode(object)), indent=2)
Notatka: że nadal nie mogą wydrukować metod obiektu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-24 11:40:01
Ta klasa potrafi zrobić sztuczkę, konwertuje obiekt do standardowego json .
import json
class Serializer(object):
@staticmethod
def serialize(object):
return json.dumps(object, default=lambda o: o.__dict__.values()[0])
Użycie:
Serializer.serialize(my_object)
Praca w python2.7
i python3
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-09 14:14:52
import json
class Foo(object):
def __init__(self):
self.bar = 'baz'
self._qux = 'flub'
def somemethod(self):
pass
def default(instance):
return {k: v
for k, v in vars(instance).items()
if not str(k).startswith('_')}
json_foo = json.dumps(Foo(), default=default)
assert '{"bar": "baz"}' == json_foo
print(json_foo)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-17 06:20:20
Jaraco dał całkiem zgrabną odpowiedź. Musiałem naprawić kilka drobnych rzeczy, ale to działa: {]}
Kod
# Your custom class
class MyCustom(object):
def __json__(self):
return {
'a': self.a,
'b': self.b,
'__python__': 'mymodule.submodule:MyCustom.from_json',
}
to_json = __json__ # supported by simplejson
@classmethod
def from_json(cls, json):
obj = cls()
obj.a = json['a']
obj.b = json['b']
return obj
# Dumping and loading
import simplejson
obj = MyCustom()
obj.a = 3
obj.b = 4
json = simplejson.dumps(obj, for_json=True)
# Two-step loading
obj2_dict = simplejson.loads(json)
obj2 = MyCustom.from_json(obj2_dict)
# Make sure we have the correct thing
assert isinstance(obj2, MyCustom)
assert obj2.__dict__ == obj.__dict__
Zauważ, że potrzebujemy dwóch kroków do załadowania. Na razie __python__
własność
nie jest używany.
Jak często to jest?
Stosując metodę AlJohri sprawdzam popularność podejść:
Serializacja (Python - > JSON):
-
to_json
: 266,595 w dniu 2018-06-27 -
toJSON
: 96,307 on 2018-06-27 -
__json__
: 8,504 w dniu 2018-06-27 -
for_json
: 6,937 w dniu 2018-06-27
Deserializacja (JSON - > Python):
-
from_json
: 226,101 w dniu 2018-06-27
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-09 11:24:31
To mi dobrze wyszło:
class JsonSerializable(object):
def serialize(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.serialize()
@staticmethod
def dumper(obj):
if "serialize" in dir(obj):
return obj.serialize()
return obj.__dict__
A następnie
class FileItem(JsonSerializable):
...
I
log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-18 08:10:39
Jeśli nie masz nic przeciwko zainstalowaniu pakietu, możesz użyć json-tricks :
pip install json-tricks
Po tym wystarczy zaimportować dump(s)
z json_tricks
zamiast json, a to zwykle zadziała:
from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)
Co da
{
"__instance_type__": [
"module_name.test_class",
"MyTestCls"
],
"attributes": {
"attr": "val",
"dct_attr": {
"hello": 42
}
}
}
I to w zasadzie wszystko!
To będzie działać świetnie w ogóle. Istnieją pewne wyjątki, np. jeśli w __new__
dzieją się specjalne rzeczy, lub dzieje się więcej magii metaklasu.
Oczywiście Ładowanie też działa (inaczej co punkt): {]}
from json_tricks import loads
json_str = loads(json_str)
To zakłada, że module_name.test_class.MyTestCls
może być importowany i nie zmienia się w niezgodny sposób. odzyskasz instancję , a nie jakiś słownik czy coś, i powinna to być identyczna Kopia do tej, którą wyrzuciłeś.
Jeśli chcesz dostosować sposób serializacji czegoś ,możesz dodać specjalne metody do swojej klasy, jak tak:
class CustomEncodeCls:
def __init__(self):
self.relevant = 42
self.irrelevant = 37
def __json_encode__(self):
# should return primitive, serializable types like dict, list, int, string, float...
return {'relevant': self.relevant}
def __json_decode__(self, **attrs):
# should initialize all properties; note that __init__ is not called implicitly
self.relevant = attrs['relevant']
self.irrelevant = 12
Który serializuje tylko część parametrów atrybutów, jako przykład.
I jako Darmowy bonus, ty get (de)serializacja tablic numpy, daty i godziny, uporządkowanych map, a także możliwość dołączania komentarzy w json.
Disclaimer: stworzyłem json_tricks, ponieważ miałem ten sam problem co Ty.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-10 12:53:50
Jsonweb wydaje się być dla mnie najlepszym rozwiązaniem. Zobacz http://www.jsonweb.info/en/latest/
from jsonweb.encode import to_object, dumper
@to_object()
class DataModel(object):
def __init__(self, id, value):
self.id = id
self.value = value
>>> data = DataModel(5, "foo")
>>> dumper(data)
'{"__type__": "DataModel", "id": 5, "value": "foo"}'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-07 05:32:17
class DObject(json.JSONEncoder):
def delete_not_related_keys(self, _dict):
for key in ["skipkeys", "ensure_ascii", "check_circular", "allow_nan", "sort_keys", "indent"]:
try:
del _dict[key]
except:
continue
def default(self, o):
if hasattr(o, '__dict__'):
my_dict = o.__dict__.copy()
self.delete_not_related_keys(my_dict)
return my_dict
else:
return o
a = DObject()
a.name = 'abdul wahid'
b = DObject()
b.name = a
print(json.dumps(b, cls=DObject))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-21 06:48:56
Budowanie na Quinten Cabo'S odpowiedź :
def sterilize(obj):
"""Make an object more ameniable to dumping as json
"""
if type(obj) in (str, float, int, bool, type(None)):
return obj
elif isinstance(obj, dict):
return {k: sterilize(v) for k, v in obj.items()}
list_ret = []
dict_ret = {}
for a in dir(obj):
if a == '__iter__' and callable(obj.__iter__):
list_ret.extend([sterilize(v) for v in obj])
elif a == '__dict__':
dict_ret.update({k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']})
elif a not in ['__doc__', '__module__']:
aval = getattr(obj, a)
if type(aval) in (str, float, int, bool, type(None)):
dict_ret[a] = aval
elif a != '__class__' and a != '__objclass__' and isinstance(aval, type):
dict_ret[a] = sterilize(aval)
if len(list_ret) == 0:
if len(dict_ret) == 0:
return repr(obj)
return dict_ret
else:
if len(dict_ret) == 0:
return list_ret
return (list_ret, dict_ret)
Różnice są
- Działa dla dowolnych iterowalnych zamiast tylko
list
ituple
(działa dla tablic NumPy, itp.) - Działa dla typów dynamicznych(tych, które zawierają
__dict__
). - Zawiera natywne typy
float
iNone
, więc nie są konwertowane na string. - klasy, które mają
__dict__
i członków, będą głównie działać (jeśli__dict__
i nazwy członków kolidują, będziesz tylko get one-prawdopodobnie członek) - klasy, które są listami i mają członków, będą wyglądały jak krotka listy i słownik
- Python3 (that
isinstance()
call may be the only thing that needs changing)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-19 11:51:01
Komentarz Kyle 'a Delaney' a jest poprawny więc próbowałem użyć odpowiedzi https://stackoverflow.com/a/15538391/1497139 oraz ulepszoną wersję https://stackoverflow.com/a/10254820/1497139
Aby utworzyć" JSONAble " mixin.
Więc aby klasy JSON serializowalne użyć "JSONAble" jako super klasy i albo wywołać:
instance.toJSON()
Lub
instance.asJSON()
Dla dwóch oferowanych metod. Możesz również rozszerzyć klasę JSONAble o inne podejścia oferowane tutaj.
Przykład testu jednostkowego z próbką rodziny i osoby wynika z:
ToJSOn():
{
"members": {
"Flintstone,Fred": {
"firstName": "Fred",
"lastName": "Flintstone"
},
"Flintstone,Wilma": {
"firstName": "Wilma",
"lastName": "Flintstone"
}
},
"name": "The Flintstones"
}
AsJSOn():
{'name': 'The Flintstones', 'members': {'Flintstone,Fred': {'firstName': 'Fred', 'lastName': 'Flintstone'}, 'Flintstone,Wilma': {'firstName': 'Wilma', 'lastName': 'Flintstone'}}}
Test jednostkowy z próbką rodzinną i osobową
def testJsonAble(self):
family=Family("The Flintstones")
family.add(Person("Fred","Flintstone"))
family.add(Person("Wilma","Flintstone"))
json1=family.toJSON()
json2=family.asJSON()
print(json1)
print(json2)
class Family(JSONAble):
def __init__(self,name):
self.name=name
self.members={}
def add(self,person):
self.members[person.lastName+","+person.firstName]=person
class Person(JSONAble):
def __init__(self,firstName,lastName):
self.firstName=firstName;
self.lastName=lastName;
Jsonable.py definiowanie JSONAble mixin
'''
Created on 2020-09-03
@author: wf
'''
import json
class JSONAble(object):
'''
mixin to allow classes to be JSON serializable see
https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable
'''
def __init__(self):
'''
Constructor
'''
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
def getValue(self,v):
if (hasattr(v, "asJSON")):
return v.asJSON()
elif type(v) is dict:
return self.reprDict(v)
elif type(v) is list:
vlist=[]
for vitem in v:
vlist.append(self.getValue(vitem))
return vlist
else:
return v
def reprDict(self,srcDict):
'''
get my dict elements
'''
d = dict()
for a, v in srcDict.items():
d[a]=self.getValue(v)
return d
def asJSON(self):
'''
recursively return my dict elements
'''
return self.reprDict(self.__dict__)
Znajdziesz te podejścia teraz zintegrowane w https://github.com/WolfgangFahl/pyLoDStorage projekt dostępny pod adresem https://pypi.org/project/pylodstorage/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-08 09:56:12
Oto moje 3 centy ...
Demonstruje to jawną serializację json dla drzewiastego obiektu Pythona.
Uwaga: jeśli rzeczywiście chcesz jakiś kod jak ten możesz użyć twisted
FilePath class.
import json, sys, os
class File:
def __init__(self, path):
self.path = path
def isdir(self):
return os.path.isdir(self.path)
def isfile(self):
return os.path.isfile(self.path)
def children(self):
return [File(os.path.join(self.path, f))
for f in os.listdir(self.path)]
def getsize(self):
return os.path.getsize(self.path)
def getModificationTime(self):
return os.path.getmtime(self.path)
def _default(o):
d = {}
d['path'] = o.path
d['isFile'] = o.isfile()
d['isDir'] = o.isdir()
d['mtime'] = int(o.getModificationTime())
d['size'] = o.getsize() if o.isfile() else 0
if o.isdir(): d['children'] = o.children()
return d
folder = os.path.abspath('.')
json.dump(File(folder), sys.stdout, default=_default)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-10 17:59:53
Napotkałem ten problem, gdy próbowałem zapisać model Peewee w PostgreSQL JSONField
.
Po jakimś czasie zmagań, oto ogólne rozwiązanie.
Kluczem do mojego rozwiązania jest przeglądanie kodu źródłowego Pythona i uświadomienie sobie, że dokumentacja kodu (opisana tutaj) wyjaśnia już, jak rozszerzyć istniejące json.dumps
, aby obsługiwać inne typy danych.
Załóżmy, że masz obecnie model, który zawiera pola, które nie są serializowalne do JSON i model, który zawiera pole JSON, wygląda oryginalnie następująco:
class SomeClass(Model):
json_field = JSONField()
Po prostu zdefiniuj zwyczaj JSONEncoder
Tak:
class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
return < whatever value you want >
return json.JSONEncoder.default(self, obj)
@staticmethod
def json_dumper(obj):
return json.dumps(obj, cls=CustomJsonEncoder)
A następnie po prostu użyj go w swoim JSONField
Jak poniżej:
class SomeClass(Model):
json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)
Kluczem jest metoda default(self, obj)
powyżej. Dla każdej pojedynczej skargi ... is not JSON serializable
otrzymanej od Pythona, po prostu dodaj kod do obsługi typu unserializable-to-JSON (takiego jak Enum
lub datetime
)
Na przykład, oto jak wspieram klasę dziedziczącą z Enum
:
class TransactionType(Enum):
CURRENT = 1
STACKED = 2
def default(self, obj):
if isinstance(obj, TransactionType):
return obj.value
return json.JSONEncoder.default(self, obj)
Wreszcie, z kodem zaimplementowanym jak powyżej, możesz po prostu przekonwertować dowolne modele Peewee na obiekt JSON-seriazable jak poniżej:
peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)
Chociaż powyższy kod był (nieco) specyficzny dla Peewee, ale myślę, że:
-
Jest to bardzo ważne, ponieważ jest to bardzo ważne.]}
- również, jeśli rozumiesz, jak działa
json.dumps
, To rozwiązanie działa również z Pythonem (sans ORM) w ogóle zbyt
Wszelkie pytania proszę pisać w komentarzach. Dzięki!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-30 15:04:32
Najpierw musimy uczynić nasz obiekt zgodny z JSON, abyśmy mogli go zrzucić używając standardowego modułu JSON. Zrobiłem tak:
def serialize(o):
if isinstance(o, dict):
return {k:serialize(v) for k,v in o.items()}
if isinstance(o, list):
return [serialize(e) for e in o]
if isinstance(o, bytes):
return o.decode("utf-8")
return o
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-27 08:06:12
Ta funkcja używa rekurencji do iteracji każdej części słownika, a następnie wywołuje metody repr () klas, które nie są typami wbudowanymi.
def sterilize(obj):
object_type = type(obj)
if isinstance(obj, dict):
return {k: sterilize(v) for k, v in obj.items()}
elif object_type in (list, tuple):
return [sterilize(v) for v in obj]
elif object_type in (str, int, bool, float):
return obj
else:
return obj.__repr__()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-12 13:34:32
Wymyśliłem własne rozwiązanie. Użyj tej metody, przekaż dowolny dokument (dict,Lista, ObjectId etc) do serializacji.
def getSerializable(doc):
# check if it's a list
if isinstance(doc, list):
for i, val in enumerate(doc):
doc[i] = getSerializable(doc[i])
return doc
# check if it's a dict
if isinstance(doc, dict):
for key in doc.keys():
doc[key] = getSerializable(doc[key])
return doc
# Process ObjectId
if isinstance(doc, ObjectId):
doc = str(doc)
return doc
# Use any other custom serializting stuff here...
# For the rest of stuff
return doc
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-21 05:06:09
Najbardziej podobała mi się metoda Lost kodera. Napotkałem problemy podczas próby serializacji bardziej złożonych obiektów, których członkowie / metody nie są serializowalne. Oto moja implementacja, która działa na większej liczbie obiektów:
class Serializer(object):
@staticmethod
def serialize(obj):
def check(o):
for k, v in o.__dict__.items():
try:
_ = json.dumps(v)
o.__dict__[k] = v
except TypeError:
o.__dict__[k] = str(v)
return o
return json.dumps(check(obj).__dict__, indent=2)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-11 05:35:52
Jeśli jesteś w stanie zainstalować pakiet, polecam wypróbowanie dill, który działał dobrze dla mojego projektu. Fajną rzeczą w tym pakiecie jest to, że ma ten sam interfejs co pickle
, więc jeśli używałeś już pickle
w swoim projekcie, możesz po prostu zastąpić dill
i sprawdzić, czy skrypt działa, bez zmiany kodu. Jest to więc bardzo tanie rozwiązanie do wypróbowania!
(Full anty-disclosure: Nie jestem w żaden sposób powiązany i nigdy nie przyczyniłem się do koperku projekt.)
Zainstaluj pakiet:
pip install dill
Następnie edytuj kod, aby zaimportować dill
zamiast pickle
:
# import pickle
import dill as pickle
Uruchom skrypt i sprawdź, czy działa. (Jeśli tak, możesz wyczyścić swój kod, aby nie zasłaniać pickle
nazwy modułu!)
Niektóre szczegóły na temat typów danych, które dill
mogą i nie mogą serializować, ze strony Strony Projektu :
dill
można wytrawić następujące standardowe typy:Brak, Typ, bool, int, long, float, complex, str, unicode, tuple, list, dict, file, buffer, builtin, zarówno klasy starego jak i nowego stylu, instancje klas Starego i nowego stylu, set, frozenset, array, funkcje, wyjątki
dill
może również marynować bardziej "egzotyczne" standardowe typy:Funkcje z plonami, funkcje zagnieżdżone, lambda, komórka, metoda, unboundmethod, module, code, methodwrapper, dictproxy, methoddescriptor, getsetdescriptor, memberdescriptor, wrapperdescriptor, xrange, slice, notimplemented, ellipsis, quit
dill
nie można jeszcze określić tych standardowych typów:Frame, generator, traceback
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-18 16:48:54
Nie widzę tutaj wzmianki o wersjonowaniu szeregowym lub backcompat, więc opublikuję moje rozwiązanie, którego używam od jakiegoś czasu. Prawdopodobnie mam dużo więcej do nauczenia się, szczególnie Java i Javascript są prawdopodobnie bardziej dojrzałe ode mnie tutaj, ale tutaj idzie
Https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-27 21:39:37
Aby dodać kolejną opcję: możesz użyć pakietu attrs
i metody asdict
.
class ObjectEncoder(JSONEncoder):
def default(self, o):
return attr.asdict(o)
json.dumps(objects, cls=ObjectEncoder)
I do konwersji z powrotem
def from_json(o):
if '_obj_name' in o:
type_ = o['_obj_name']
del o['_obj_name']
return globals()[type_](**o)
else:
return o
data = JSONDecoder(object_hook=from_json).decode(data)
Klasa wygląda tak
@attr.s
class Foo(object):
x = attr.ib()
_obj_name = attr.ib(init=False, default='Foo')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-15 17:12:35