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

Author: FraggaMuffin, 2010-09-22

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>
>>> 
 611
Author: Manoj Govindan,
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"
}
 696
Author: Onur Yıldırım,
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.

(link do jsonpickle na PyPi)

 192
Author: gecco,
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.

 113
Author: andyhasit,
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)
 55
Author: Jason S,
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"}'
 41
Author: Paulo Freitas,
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.

 38
Author: Fancy John,
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
 27
Author: R H,
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"
}
 26
Author: tobigue,
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))
 12
Author: tryer3000,
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.

 12
Author: user1587520,
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.

 8
Author: ribamar,
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.

 5
Author: Lost Koder,
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)
 4
Author: rectangletangle,
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):

Deserializacja (JSON - > Python):

 4
Author: Martin Thoma,
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))
 4
Author: jmhostalet,
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.

 3
Author: Mark,
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"}'
 2
Author: matthewlent,
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))
 2
Author: Sheikh Abdul Wahid,
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ą

  1. Działa dla dowolnych iterowalnych zamiast tylko list i tuple (działa dla tablic NumPy, itp.)
  2. Działa dla typów dynamicznych(tych, które zawierają __dict__).
  3. Zawiera natywne typy float i None, więc nie są konwertowane na string.
  4. 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)
  5. klasy, które są listami i mają członków, będą wyglądały jak krotka listy i słownik
  6. Python3 (that isinstance() call may be the only thing that needs changing)
 2
Author: mheyman,
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/

 2
Author: Wolfgang Fahl,
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)
 1
Author: Dan Brough,
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.]}
  1. 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!

 1
Author: sivabudh,
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
 1
Author: Adi Degani,
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__()
 1
Author: Quinten Cabo,
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
 0
Author: Dewsworld,
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)
 0
Author: Will Charlton,
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

 0
Author: thedavidmo,
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

 0
Author: Fletch F Fletch,
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')
 0
Author: machinekoder,
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