Bir sınıf JSON serileştirilebilir nasıl yapılır


833

Python sınıfı nasıl serileştirilebilir hale getirilir?

Basit bir sınıf:

class FileItem:
    def __init__(self, fname):
        self.fname = fname

Çıktı elde edebilmek için ne yapmalıyım:

>>> import json

>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable

Hatasız


31
Cevapların hepsinin "Bir sınıfı nasıl serileştirebilirim?" "Bir sınıfı serileştirilebilir hale nasıl getirebilirim?" Bu cevaplar, nesneyi serileştiren başka bir modüle iletmek yerine serileştirmeyi kendiniz yaptığınızı varsayar.
Kyle Delaney

Python3.5 + kullanıyorsanız jsons kullanabilirsiniz. Nesnenizi (ve özniteliklerini özyinelemeli olarak ) bir dikteye dönüştürecektir. import jsonsaşağıdaki cevaba bakın - mükemmel çalışıyor
tswaehn

Yanıtlar:


551

Beklenen çıktı hakkında bir fikriniz var mı? Örneğin, bu işe yarar mı?

>>> f  = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'

Bu durumda sadece arayabilirsiniz json.dumps(f.__dict__).

Daha fazla özelleştirilmiş çıktı istiyorsanız, JSONEncoderkendi özel serileştirmenizi alt sınıflara ayırmanız ve uygulamanız gerekir.

Önemsiz bir örnek için aşağıya bakın.

>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
        def default(self, o):
            return o.__dict__    

>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'

Sonra bu sınıfı kwarg json.dumps()olarak clsyönteme geçirirsiniz:

json.dumps(cls=MyEncoder)

Ayrıca kodunu çözmek istiyorsanız object_hook, JSONDecodersınıfa bir özel sağlamanız gerekir . Örneğin;

>>> 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>
>>> 

44
Kullanmak __dict__her durumda çalışmaz. Öznitelikler nesne somutlaştırıldıktan sonra ayarlanmamışsa, __dict__tam olarak doldurulmayabilir. Yukarıdaki örnekte, tamamsınız, ancak kodlamak istediğiniz sınıf öznitelikleriniz varsa, __dict__bunlar sınıf __init__çağrısında veya nesne somutlaştırıldıktan sonra başka bir şekilde değiştirilmedikçe listelenmez .
Kris Hardy

8
+1, ancak from_json()nesne kanca olarak kullanılan fonksiyonun bir else: return json_objectifadesi olmalıdır , böylece genel nesnelerle de başa çıkabilir.
jogojapan

8
@KrisHardy __dict__, __slots__yeni bir stil sınıfında kullanırsanız da çalışmaz .
badp

7
Yöntemin JSONEncodervarlığını kontrol __json_serializable__etmek ve nesnenin JSON serileştirilebilir temsilini elde etmek için çağırmak gibi özel bir protokol oluşturmak için yukarıdaki gibi bir özel kullanabilirsiniz . Bu, diğer Python desenleri gibi doğrultusunda olacağını __getitem__, __str__, __eq__, ve __len__.
jpmc26

5
__dict__ayrıca özyinelemeli olarak çalışmaz, örneğin nesnenizin bir özelliği başka bir nesne ise.
Neel

634

İşte basit bir özellik için basit bir çözüm:

.toJSON() Yöntem

JSON serileştirilebilir sınıfı yerine, serileştirici yöntemi uygulayın:

import json

class Object:
    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, 
            sort_keys=True, indent=4)

Yani sadece serileştirmek için çağırırsınız:

me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"

print(me.toJSON())

çıktı olacak:

{
    "age": 35,
    "dog": {
        "name": "Apollo"
    },
    "name": "Onur"
}

82
Çok sınırlı. {"Foo": "bar", "baz": "bat"} gibi bir dikteniz varsa, bu JSON'a kolayca serileştirilir. Bunun yerine {"foo": "bar", "baz": MyObject ()} varsa, yapamazsınız. İdeal durum, iç içe geçmiş nesnelerin JSON'a açıkça değil, yinelemeli olarak serileştirilmesidir.
Mark E. Haase

30
Yine de işe yarayacak. Kayboldun o.__dict___. Kendi örneğinizi deneyin: class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
Onur Yıldırım

14
Bu çözüm tersine çevrilebilir mi? Yani nesneyi json'dan yeniden yapılandırmak kolay mı?
Jorge Leitao

2
@ JCLeitão Hayır. Aynı alanlara sahip iki farklı sınıfınız olabilir. Bu sınıfın a ve b nesneleri (muhtemelen aynı özelliklere sahip) aynı a.__dict__/ değerine sahip olacaktır b.__dict__.
Martin Thoma

7
Bu, datetime.datetimeörneklerle çalışmaz . Aşağıdaki hatayı atar:'datetime.datetime' object has no attribute '__dict__'
Bruno Finger

171

Daha karmaşık sınıflar için jsonpickle aracını düşünebilirsiniz :

jsonpickle, karmaşık Python nesnelerinin JSON'a gönderilmesi ve JSON'dan serileştirilmesi için bir Python kütüphanesidir.

Stytlib'in json, simplejson ve demjson gibi Python'u PONON'a kodlamak için kullanılan standart Python kütüphaneleri, yalnızca doğrudan JSON eşdeğeri olan Python ilkellerini (örn. Dikteler, listeler, dizeler, ints, vs.) işleyebilir. jsonpickle bu kütüphanelerin üzerine inşa edilir ve daha karmaşık veri yapılarının JSON'a serileştirilmesine izin verir. jsonpickle son derece yapılandırılabilir ve genişletilebilir - kullanıcının JSON arka ucunu seçmesine ve ek arka uçlar eklemesine izin verir.

(PyPi'deki jsonpickle bağlantısı)


32
C # geliyor, bu bekliyordum. Basit bir astar ve sınıflarla uğraşmak yok.
Jerther

2
jsonpickle harika. Birçok sınıf seviyesine sahip büyük, karmaşık, dağınık bir nesne için mükemmel çalıştı
wisbucky

bunu bir dosyaya kaydetmenin uygun bir yolu var mı? Belgeler yalnızca bir jsonpicklenesnenin nasıl kodlanacağını ve kodlarının çözüleceğini gösterir . Ayrıca, bu panda veri çerçeveleri içeren bir dikte kodunun kodunu çözemedi.
user5359531

3
@ user5359531 kullanabilirsiniz obj = jsonpickle.decode(file.read())ve file.write(jsonpickle.encode(obj)).
Kilian Batzner

1
Özellikle django için bir soru: jsonpickle'ın oturum verilerini serileştirmek için kullanımı turşu ile aynı güvenlik açığına sahip mi? (burada açıklandığı gibi docs.djangoproject.com/en/1.11/topics/http/sessions/… )?
Paul Bormans

89

Yanıtların çoğu , her zaman mümkün olmayan veya arzu edilmeyen json.dumps () çağrısını değiştirmeyi içerir ( örneğin, bir çerçeve bileşeninin içinde olabilir).

Eğer arama yapabilmek istiyorsanız json.dumps (obj) sonra basit bir çözüm devralmakta olduğu gibi 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

Bu, sınıfınız yalnızca temel veri temsiliyse işe yarar, daha zor şeyler için anahtarları her zaman açıkça ayarlayabilirsiniz.


2
Bu gerçekten güzel bir çözüm olabilir :) Benim durumum için olduğuna inanıyorum. Faydaları: nesnenin "şeklini" init ile bir sınıf haline getirerek iletirsiniz, doğal olarak serileştirilebilir ve repr olarak yorumlanabilir görünür .
PascalVKooten

1
"Dot-access" hala eksik olmasına rağmen :(
PascalVKooten

2
Ahh bu işe yarıyor! Teşekkürler, bunun neden kabul edilen cevap olmadığından emin değilim. Bunu değiştirmenin dumpsiyi bir çözüm olmadığına tamamen katılıyorum . Bu arada, çoğu durumda muhtemelen dictyetki devri ile birlikte miras almak istiyorsunuz , yani dictsınıfınızda bir tür öznitelik olacak, daha sonra bu özniteliği parametre olarak başlatma gibi bir şey olarak geçireceksiniz super().__init__(self.elements).
cglacet

47

Onur'un cevabını seviyorum ama toJSON()nesnelerin serileştirilmesi için isteğe bağlı bir yöntem içerecek şekilde genişleyecekti :

def dumper(obj):
    try:
        return obj.toJSON()
    except:
        return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)

Bunu, mevcut json.dumpskullanım ile özel işleme giriş arasında en iyi denge olarak buldum . Teşekkürler!
Daniel Buckmaster

12
Aslında bunu gerçekten seviyorum; ama try-catchbüyük olasılıkla if 'toJSON' in obj.__attrs__():sessiz bir hatadan (toJSON () 'da orada olmamaktan başka bir nedenden dolayı) kaçınmak ... potansiyel olarak veri bozulmasına yol açacak bir hata yapmaktan ziyade.
thclark

39

Başka bir seçenek de JSON dampingini kendi sınıfına sarmaktır:

import json

class FileItem:
    def __init__(self, fname):
        self.fname = fname

    def __repr__(self):
        return json.dumps(self.__dict__)

Veya daha da iyisi, bir sınıftan FileItem sınıfını alt sınıflandırma 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

Test yapmak:

>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'

2
Merhaba, ben gerçekten bu "özel kodlayıcı" yaklaşım sevmiyorum, u sınıf json seriazable yapabilirsiniz daha iyi olurdu. Deniyorum, deniyorum ve deniyorum ve hiçbir şey. Bunun nasıl yapılacağı hakkında bir fikrin var mı? Mesele şu ki, json modülü yerleşik python tiplerine karşı sınıfınızı test ediyor ve hatta özel sınıflar için kodlayıcınızı oluşturduğunu söylüyor :). Taklit edilebilir mi? Yani benim sınıf için bir şey yapabilirim böylece json modülüne basit bir liste gibi davranıyor? Ben alt sınıf kontrol ve instancecheck ama hiçbir şey denemek .
Bojan Radojevic

@ ADRENALIN Tüm sınıf öznitelik değerleri serileştirilebilirse ve bilgisayar korsanlıklarına aldırmazsanız, birincil türden (muhtemelen dikte) devralınabilirsiniz. Ayrıca standart olan yerine jsonpickle veya json_tricks veya başka bir şey kullanabilirsiniz (yine de özel bir kodlayıcı, ancak yazmanız veya aramanız gereken bir kod değil). İlk örnek turşu, ikincisi bunu __json__encode__/ __json_decode__(açıklama: sonuncuyu yaptım) uygulayarak değiştirebileceğiniz nitelikler göstergesi olarak saklar .
Mark

30

Sadece to_jsonsınıfınıza şöyle bir yöntem ekleyin :

def to_json(self):
  return self.message # or how you want it to be serialized

Ve bu kodu ( bu yanıttan ) , her şeyin üstünde bir yere ekleyin :

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder().default
JSONEncoder.default = _default

JSONEncoder.default () otomatik olarak özel bir "to_json ()" yöntemini denetler ve bulursa nesneyi kodlamak için kullanırsa, json modülü içe aktarılır.

Aynen Onur'un dediği gibi, ama bu sefer json.dumps()projenizdeki her şeyi güncellemek zorunda değilsiniz .


6
Çok teşekkürler! İstediğimi yapmama izin veren tek cevap bu: mevcut kodu değiştirmeden bir nesneyi serileştirebiliyorum. Diğer yöntemler çoğunlukla benim için çalışmıyor. Nesne bir üçüncü taraf kitaplığında tanımlanır ve serileştirme kodu da üçüncü taraftır. Onları değiştirmek garip olacaktır. Metodunuzla sadece yapmam gerekiyor TheObject.to_json = my_serializer.
Yongwei Wu

24

Geçen gün bu sorunla karşılaştım ve iç içe nesneleri ve devralınan alanları işleyebilen Python nesneleri için bir Kodlayıcı'nın daha genel bir sürümünü uyguladım :

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

Misal:

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)

Sonuç:

{
  "a": "A", 
  "b": [
    {
      "ab": {
        "b": "B", 
        "i": "I", 
        "y": "y"
      }
    }
  ], 
  "c": {
    "c": "YES"
  }, 
  "i": "I"
}

1
Bu biraz eski olmasına rağmen .. Bazı dairesel ithalat hatası ile karşı karşıyayım. return objSon satır yerine bunu yaptım return super(ObjectEncoder, self).default(obj). Referans HERE
SomeTypeFoo

24

Python3.5 + kullanıyorsanız, kullanabilirsiniz jsons. Nesnenizi (ve özniteliklerini özyinelemeli olarak) bir dikteye dönüştürecektir.

import jsons

a_dict = jsons.dump(your_object)

Veya bir dize istiyorsanız:

a_str = jsons.dumps(your_object)

Veya sınıfınız uygulanmışsa jsons.JsonSerializable:

a_dict = your_object.json

3
Python 3.7+ kullanabiliyorsanız, python sınıflarını dikte ve JSON dizelerine (ve viceversa) dönüştürmek için en temiz çözümün jsonskütüphaneyi veri dosyalarıyla karıştırmak olduğunu buldum . Şimdiye kadar, benim için çok iyi!
Ruluk

3
Bu, standart Python kurulumunda yerleşik olmayan harici bir kütüphanedir.
Noumenon

sadece yuva özelliği olan sınıf için
yehudahs

Yapabilirsiniz, ancak yuva kullanmanıza gerek yoktur . Sadece belirli bir sınıfın imzasına göre damping yaparken yuvalara ihtiyacınız olacak . Önümüzdeki 1.1.0 sürümünde artık durum böyle değil.
RH

11
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', 'alice@mail.com')))

standart kullanırsanız json, bir defaultişlev tanımlamanız gerekir

import json
def default(o):
    return o._asdict()

print(json.dumps(User('alice', 'alice@mail.com'), default=default))

2
Bunu lambda ile _asdict işlevini kaldırarak basitleştirdim json.dumps(User('alice', 'alice@mail.com'), default=lambda x: x.__dict__)
JustEngland

8

jsonyazdırabildiği nesneler açısından sınırlıdır ve jsonpickle(girmeniz gerekebilir pip install jsonpickle) metni girintilemediği açısından sınırlıdır. Sınıfını değiştiremeyeceğiniz bir nesnenin içeriğini incelemek isterseniz, yine de daha düz bir yol bulamadım:

 import json
 import jsonpickle
 ...
 print  json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

Not: yine de nesne yöntemlerini yazdıramazlar.


6

Bu sınıf hile yapabilir, nesneyi standart json'a dönüştürür.

import json


class Serializer(object):
    @staticmethod
    def serialize(object):
        return json.dumps(object, default=lambda o: o.__dict__.values()[0])

kullanımı:

Serializer.serialize(my_object)

çalışma python2.7ve python3.


Bu yöntemi en çok sevdim. Üyeleri / yöntemleri serileştirilemeyen daha karmaşık nesneleri serileştirmeye çalışırken sorunlarla karşılaştım. İşte daha fazla nesne üzerinde çalışan benim uygulama: `` sınıf Serializer (nesne): @staticmethod def serialize (obj): def check (o): k, v için o .__ dict __. İtems (): try: _ = json .dumps (v) o .__ dict __ [k] = v TypeError hariç: o .__ dict __ [k] = str (v) return o return json.dumps (kontrol (obj) .__ dict__, girinti = 2) ``
Will Charlton

4
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)

Doc Gönderen : Parametre default(obj), nesnelleştirilebilir bir sürümü döndürülmesi veya TypeError yükseltmesi gereken bir işlevdir. Varsayılan default, TypeError öğesini yükseltir.
luckydonald

4

jaraco oldukça düzgün bir cevap verdi. Bazı küçük şeyleri düzeltmem gerekiyordu, ancak bu işe yarıyor:

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__

Yükleme için iki adıma ihtiyacımız olduğunu unutmayın. Şimdilik __python__özellik kullanılmıyor.

Bu ne kadar yaygın?

AlJohri yöntemini kullanarak, yaklaşımların popülaritesini kontrol ediyorum:

Serileştirme (Python -> JSON):

  • to_json: 2018-06-27 tarihinde 266.595
  • toJSON: 96.307 2018-06-27 tarihinde
  • __json__: 2018-06-27 tarihinde 8.504
  • for_json: 2018-06-27 tarihinde 6.937

Diziselleştirme (JSON -> Python):


4

Bu benim için iyi çalıştı:

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__

ve sonra

class FileItem(JsonSerializable):
    ...

ve

log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))

3

Bunun için bir paket kurmanın sakıncası yoksa, json-tricks kullanabilirsiniz :

pip install json-tricks

Bundan sonra sadece ithalat gerek dump(s)dan json_tricksyerine json ve çalışma genellikle sonraki adımda:

from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)

hangisi verecek

{
        "__instance_type__": [
                "module_name.test_class",
                "MyTestCls"
        ],
        "attributes": {
                "attr": "val",
                "dct_attr": {
                        "hello": 42
                }
        }
}

Ve temelde bu!


Bu genel olarak harika çalışır. Bazı istisnalar vardır, örneğin özel şeyler meydana gelirse __new__veya daha fazla metasınıf büyüsü devam ederse .

Açıkçası yükleme de işe yarıyor (aksi takdirde amaç nedir):

from json_tricks import loads
json_str = loads(json_str)

Bu, module_name.test_class.MyTestClsiçe aktarılabileceğini ve uyumlu olmayan şekillerde değiştirilmediğini varsayar . Bir sözlüğü değil, bir örneği geri alırsınız ve döktüğünüzle aynı kopya olmalıdır.

Bir şeyin nasıl serileştirildiğini (de) özelleştirmek istiyorsanız, sınıfınıza aşağıdaki gibi özel yöntemler ekleyebilirsiniz:

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

örnek olarak nitelik parametrelerinin yalnızca bir kısmını serileştirir.

Ve ücretsiz bir bonus olarak, numpy dizilerin, tarih ve saatlerin, sıralı haritaların serileştirilmesini ve json'a yorum ekleme yeteneğini elde edersiniz.

Feragatname: Sizinle aynı problemim olduğu için json_tricks oluşturdum .


1
Json_tricks'i test ettim ve güzelleştirdi (2019'da).
pauljohn32

2

jsonweb benim için en iyi çözüm gibi görünüyor. Bkz. 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"}'

İç içe nesneler için iyi çalışıyor mu? Kod çözme ve kodlama dahil
Simone Zandara

1

İşte benim 3 sent ...
Bu ağaç benzeri bir python nesnesi için açık json serileştirme gösterir.
Not: Aslında böyle bir kod istiyorsanız, bükülmüş FilePath sınıfını kullanabilirsiniz.

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

Peewee'nin modelini PostgreSQL'e kaydetmeye çalıştığımda bu problemle karşılaştım JSONField.

Bir süre mücadele ettikten sonra, işte genel çözüm.

Çözümümün anahtarı Python'un kaynak kodundan geçiyor ve kod belgelerinin ( burada açıklanan ) zaten mevcut json.dumpsolanın diğer veri türlerini destekleyecek şekilde nasıl genişletileceğini açıkladığını fark etmektir .

Geçerli olarak, JSON için serileştirilemeyen bazı alanları içeren bir modeliniz olduğunu ve JSON alanını içeren modelin aslında şöyle göründüğünü varsayalım:

class SomeClass(Model):
    json_field = JSONField()

Bunun JSONEncodergibi bir özel tanımlamanız yeterlidir :

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)

Ve sonra JSONFieldaşağıdaki gibi kullanın :

class SomeClass(Model):
    json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)

Anahtar default(self, obj)yukarıdaki yöntemdir. ... is not JSON serializablePython'dan aldığınız her bir şikayet için, serileştirilemeyen-JSON türünü ( Enumveya gibi datetime) işlemek için kod eklemeniz yeterlidir.

Örneğin, nasıl bir sınıf devralan destek burada 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)

Son olarak, kod yukarıdaki gibi uygulandığında, herhangi bir Peewee modelini aşağıdaki gibi bir JSON seriazable nesnesi haline dönüştürebilirsiniz:

peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)

Yukarıdaki kod (biraz) Peewee özgü olsa da, ama bence:

  1. Genel olarak diğer ORM'ler (Django vb.)
  2. Ayrıca, nasıl json.dumpsçalıştığını anladıysanız , bu çözüm genel olarak Python (sans ORM) ile de çalışır

Herhangi bir sorunuz varsa, lütfen yorum bölümüne gönderin. Teşekkürler!


1

Bu işlev, sözlüğün her bölümünü yinelemek için özyineleme kullanır ve ardından yerleşik tür olmayan sınıfların repr () yöntemlerini çağırır .

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):
        return obj
    else:
        return obj.__repr__()


0

Kendi çözümümü buldum. Bu yöntemi kullanın, serileştirmek için herhangi bir belgeyi ( dict , list , ObjectId vb.) İletin .

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

Dtetime nesnesi serileştirme sorununu çözmek için dekoratörleri kullanmayı seçtim. İşte benim kod:

#myjson.py
#Author: jmooremcc 7/16/2017

import json
from datetime import datetime, date, time, timedelta
"""
This module uses decorators to serialize date objects using json
The filename is myjson.py
In another module you simply add the following import statement:
    from myjson import json

json.dumps and json.dump will then correctly serialize datetime and date 
objects
"""

def json_serial(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, (datetime, date)):
        serial = str(obj)
        return serial
    raise TypeError ("Type %s not serializable" % type(obj))


def FixDumps(fn):
    def hook(obj):
        return fn(obj, default=json_serial)

    return hook

def FixDump(fn):
    def hook(obj, fp):
        return fn(obj,fp, default=json_serial)

    return hook


json.dumps=FixDumps(json.dumps)
json.dump=FixDump(json.dump)


if __name__=="__main__":
    today=datetime.now()
    data={'atime':today, 'greet':'Hello'}
    str=json.dumps(data)
    print str

Yukarıdaki modülü içe aktararak, diğer modüllerim tarih saati nesneleri içeren verileri serileştirmek için json'u normal bir şekilde (varsayılan anahtar sözcüğü belirtmeden) kullanır. Datetime serileştirici kodu otomatik olarak json.dumps ve json.dump için çağrılır.


0

En çok Lost Koder'in yöntemini beğendim. Üyeleri / yöntemleri serileştirilemeyen daha karmaşık nesneleri serileştirmeye çalışırken sorunlarla karşılaştım. İşte benim daha fazla nesne üzerinde çalışan benim uygulama:

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

Bir paket kurabiliyorsanız, dereotu denemenizi tavsiye ederim , bu da projem için iyi çalıştı. Bu paketle ilgili güzel bir şey, arayüzüyle aynı arayüze sahip olmasıdır pickle, bu nedenle pickleprojenizde zaten kullanıyorsanız , dillherhangi bir kodu değiştirmeden komut dosyasının yerine çalışabilir ve çalışıp çalışmadığını görebilirsiniz. Bu yüzden denemek için çok ucuz bir çözüm!

(Tam açıklama karşıtı: Hiçbir şekilde dereotu projesine bağlı değilim ve katkıda bulunmadım.)

Paketi yükleyin:

pip install dill

Sonra ithal etmek kodunuzu düzenlemek dillyerine pickle:

# import pickle
import dill as pickle

Senaryonuzu çalıştırın ve çalışıp çalışmadığına bakın. (Eğer öyleyse, artık picklemodül adını gölgelendirmemek için kodunuzu temizlemek isteyebilirsiniz !)

Proje sayfasındandill serileştirilebilen ve serileştirilemeyen veri türleriyle ilgili bazı özellikler :

dill aşağıdaki standart türleri seçebilir:

hiçbiri, türü, bool, int, uzun, kayan nokta, karmaşık, str, unicode, demet, liste, dikte, dosya, arabellek, yerleşik, hem eski hem de yeni stil sınıfları, eski ve yeni stil sınıflarının örnekleri, ayarla, frozenset, dizi , işlevler, istisnalar

dill daha fazla 'egzotik' standart tip de seçebilir:

verim ile fonksiyonlar, iç içe fonksiyonlar, lambdas, hücre, yöntem, bağlanmamış yöntem, modül, kod, yöntem sarıcı, diktekoksi, yöntem tanımlayıcı, getetdescriptor, üye tanımlayıcı, sarıcı tanımlayıcı, xrange, dilim, örneklenmemiş, elips, çık

dill şu standart türleri henüz seçememektedir:

çerçeve, jeneratör, geri izleme



0

Başka bir seçenek eklemek için: attrsPaketi ve asdictyöntemi kullanabilirsiniz.

class ObjectEncoder(JSONEncoder):
    def default(self, o):
        return attr.asdict(o)

json.dumps(objects, cls=ObjectEncoder)

ve geri dönüşümü

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)

sınıf böyle görünüyor

@attr.s
class Foo(object):
    x = attr.ib()
    _obj_name = attr.ib(init=False, default='Foo')

0

Onur'un cevabına ek olarak, muhtemelen aşağıdaki gibi datetime türüyle uğraşmak istersiniz.
('datetime.datetime' nesnesinin ' dict ' istisnası özelliği yoktur .)

def datetime_option(value):
    if isinstance(value, datetime.date):
        return value.timestamp()
    else:
        return value.__dict__

Kullanımı:

def toJSON(self):
    return json.dumps(self, default=datetime_option, sort_keys=True, indent=4)

0

Öncelikle nesnemizi JSON uyumlu hale getirmeliyiz, böylece standart JSON modülünü kullanarak onu dökebiliriz. Bu şekilde yaptım:

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

0

Bina Quinten Cabo bireyin cevabı :

def sterilize(obj):
    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()}
    elif hasattr(obj, '__iter__') and callable(obj.__iter__):
        return [sterilize(v) for v in obj]
    elif hasattr(obj, '__dict__'):
        return {k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']}
    else:
        return repr(obj)

Farklar

  1. Sadece listve yerine herhangi bir yinelenebilir için çalışırtuple için çalışır (NumPy dizileri vb. İçin çalışır)
  2. Dinamik türler için çalışır ( __dict__ ).
  3. Yerel türleri içerir floatve Noneböylece dizeye dönüştürülmezler.

Okuyucuya bir alıştırma olarak, __slots__hem yinelenebilir hem de üyeleri olan sınıflar, sözlükler ve ayrıca üyeleri olan sınıflar vb.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.