Serializing class instance to JSON
Solution 1
The basic problem is that the JSON encoder json.dumps()
only knows how to serialize a limited set of object types by default, all built-in types. List here: https://docs.python.org/3.3/library/json.html#encoders-and-decoders
One good solution would be to make your class inherit from JSONEncoder
and then implement the JSONEncoder.default()
function, and make that function emit the correct JSON for your class.
A simple solution would be to call json.dumps()
on the .__dict__
member of that instance. That is a standard Python dict
and if your class is simple it will be JSON serializable.
class Foo(object):
def __init__(self):
self.x = 1
self.y = 2
foo = Foo()
s = json.dumps(foo) # raises TypeError with "is not JSON serializable"
s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2}
The above approach is discussed in this blog posting:
Serializing arbitrary Python objects to JSON using _dict_
And, of course, Python offers a built-in function that accesses .__dict__
for you, called vars()
.
So the above example can also be done as:
s = json.dumps(vars(foo)) # s set to: {"x":1, "y":2}
Solution 2
There's one way that works great for me that you can try out:
json.dumps()
can take an optional parameter default where you can specify a custom serializer function for unknown types, which in my case looks like
def serialize(obj):
"""JSON serializer for objects not serializable by default json code"""
if isinstance(obj, date):
serial = obj.isoformat()
return serial
if isinstance(obj, time):
serial = obj.isoformat()
return serial
return obj.__dict__
First two ifs are for date and time serialization
and then there is a obj.__dict__
returned for any other object.
the final call looks like:
json.dumps(myObj, default=serialize)
It's especially good when you are serializing a collection and you don't want to call __dict__
explicitly for every object. Here it's done for you automatically.
So far worked so good for me, looking forward for your thoughts.
Solution 3
You can specify the default
named parameter in the json.dumps()
function:
json.dumps(obj, default=lambda x: x.__dict__)
Explanation:
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
(Works on Python 2.7 and Python 3.x)
Note: In this case you need instance
variables and not class
variables, as the example in the question tries to do. (I am assuming the asker meant class instance
to be an object of a class)
I learned this first from @phihag's answer here. Found it to be the simplest and cleanest way to do the job.
Solution 4
Using jsonpickle
import jsonpickle
object = YourClass()
json_object = jsonpickle.encode(object)
Solution 5
I just do:
data=json.dumps(myobject.__dict__)
This is not the full answer, and if you have some sort of complicated object class you certainly will not get everything. However I use this for some of my simple objects.
One that it works really well on is the "options" class that you get from the OptionParser module. Here it is along with the JSON request itself.
def executeJson(self, url, options):
data=json.dumps(options.__dict__)
if options.verbose:
print data
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
return requests.post(url, data, headers=headers)
Related videos on Youtube
Comments
-
ferhan over 1 year
I am trying to create a JSON string representation of a class instance and having difficulty. Let's say the class is built like this:
class testclass: value1 = "a" value2 = "b"
A call to the json.dumps is made like this:
t = testclass() json.dumps(t)
It is failing and telling me that the testclass is not JSON serializable.
TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable
I have also tried using the pickle module :
t = testclass() print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))
And it gives class instance information but not a serialized content of the class instance.
b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'
What am I doing wrong?
-
CodeClown42 almost 12 years
-
codeman48 over 6 yearsUse one line,
s = json.dumps(obj, default=lambda x: x.__dict__)
, to serialize object's instance variables (self.value1
,self.value2
, ...). Its the simplest and the most straight forward way. It will serialize nested object structures. Thedefault
function is called when any given object is not directly serializable. You can also look at my answer below. I found the popular answers unnecessarily complex, which were probably true quite a long time back. -
martineau about 6 yearsYour
testclass
has no__init__()
method, so all instances will share the same two class attributes (value1
andvalue2
) defined in the class statement. Do you understand the difference between a class and an instance of one? -
best wishes almost 5 yearsThere is a python library for this github.com/jsonpickle/jsonpickle (commenting since answer is too below in the thread and wont be reachable.)
-
-
ferhan almost 12 yearsI tried this. The end result of a call to json.dumps(t.__dict__) is just {}.
-
steveha almost 12 yearsThat is because your class doesn't have a
.__init__()
method function, so your class instance has an empty dictionary. In other words,{}
is the correct result for your example code. -
ferhan almost 12 yearsThanks. This does the trick. I added a simple init with no parameters and now calling the json.dumps(t.__dict__) returns proper data in the format of: {"value2": "345", "value1": "123"} I had seen posts like this before, wasn't sure whether I needed a custom serializer for members, needing init wasn't mentioned explicitly or I missed it. Thank you.
-
SpiRail almost 11 yearsYou might want to remove self, if you are not using this inside a class.
-
Haroldo_OK almost 10 yearsThe main problem I see with pickle is that it's a Python-specific format, while JSON is a platform-independant format. JSON is specially useful if you're writing either a web application or a backend for some mobile application. That having been said, thanks for pointing out to jsonpickle.
-
Haroldo_OK almost 10 yearsThat will work okay, as long as the object isn't composed of other objects.
-
Caelum over 8 years@Haroldo_OK Doesn't jsonpickle still export to JSON, just not very human readable?
-
Nwawel A Iroume over 8 yearsThis work for a single class but not with related classes objets
-
gies0r about 7 years@NwawelAIroume: True. If you have an object which e.g. is containing multiple objects in a list the error is still
is not JSON serializable
-
pattyd over 6 years@gies0r so, in that case how could you get the json of those multiple objects?
-
Bikash Gyawali about 6 yearsCan someone provide a good reference for the approach of inheriting from JSONEncoder and then implementing the JSONEncoder.default() function?
-
Bikash Gyawali about 6 yearsAlso, what do I do if I have to get the JSON representation of not just 1 object but a list of objects?
-
H S Rathore over 5 yearsthe
__dict__
approach, Works like a charm, just the class name gets appended to the attribute names. Which in my case is ok. Using Python3.5 on Mac -
Dakota Hawkins over 5 yearsThis worked for me, but because of datetime.date members I changed it slightly:
default=lambda x: getattr(x, '__dict__', str(x))
-
codeman48 over 5 years@Dakota nice work-around;
datetime.date
is a C implementation hence it has no__dict__
attribute. IMHO for uniformity's sake,datetime.date
should be having it... -
Kyle Delaney about 5 yearsI get
NameError: name 'serialize' is not defined
. Any tips? -
Fantastory almost 5 yearsVery nice. Just for classes that have slots:
try: dict = obj.__dict__ except AttributeError: dict = {s: getattr(obj, s) for s in obj.__slots__ if hasattr(obj, s)} return dict
-
naught101 about 3 yearsThis leads to some weird outcomes, which are probably OK, just confusing:
r = Response(200, 'blah'); r['a'] = 1; r.b = 2
results inr == {'status_code': 200, 'body': 'blah', 'a': 1}
andr.__dict__ == {'b': 2}
. I would love to know wheredict
actually stores it's keys and values. -
Tom almost 3 yearsHey @GBGOLC, Thanks for that awesome piece of code! I've tried to implement it with json.loads and dumps since there aren't any files where I'm using the code. I'm struggling to decode the Vehicle class in my example, which refers to wheels. Here's how I've changed it. Help much appreciated.
-
Tom almost 3 yearsOk, I solved my own problem: since decode_ changes the instance it is passed, there was no return value to pass to a new instance. Your code was fine and only my way of calling decode_ needed changing. Thanks again for the great code!
-
SagiZiv over 2 yearsI love it. This is amazing implementation. I have just 1 question if I may, why did you use call method and not new method in the meta class? I ask, because the call method gets called every time a new instance is created, so the code tries to register the class for every new instance. Not a big deal, and as I wrote I love this implementation it seems really great :-)
-
Tarynn about 2 yearsThis is the correct answer. The class knows how to serialize itself. It is too bad that you can't just pass the method name directly to json.dumps
-
Tarynn about 2 yearsThe question isn't about serialization, it is about serializing to JSON. There are a lot of reasons for doing that and pickle would be a horrible tool for most of them.