Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: <object> is not JSON serializable

I am trying to encode an object into json using json.dumps() in Django, however when I pass in a python object, it raises this error.

TypeError: <OrgInvite: OrgInvite object> is not JSON serializable

I was under the assumption that even though JSON can only encode certain data types, one of those data types were objects. I read another question on Stack Overflow that a good way to get around this is by creating a dictionary out of the object using .__dict__ I tried this and it is saying that one of the keys in my new dictionary, _state is not serializable. I am not sure where this _state key came from, and was wondering is there a way to convert my object into a dictionary without that extra field, so I can encode it into JSON ?

model:

class OrgInvite(models.Model):
    token = models.CharField(max_length=16, unique=True, null=False)
    account_id = models.ForeignKey(Account, on_delete=models.CASCADE, null=False)
    org_id = models.ForeignKey(Org, on_delete=models.CASCADE, null=False)
    used = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)
    name = models.CharField(max_length=70)
    email = models.CharField(max_length=255)

view:

def get_invite(token):
    if not token:
        raise Exception("Invitation token is not specified")

    invitation = OrgInvite.objects.get(token=token)
    if not invitation:
        raise Exception("Invitation token is invalid.")

    return invitation

def invite_accept_redirect(token):
    # """ -Redirects to the accept invite frontend view with pre-fetched data. """

    try:
        invite = get_invite(token)
        if not invite:
            raise Exception("Invitation token is invalid")
        if invite.used:
            invite = {'used': True}
    except:
        invite = {'invalid': True}
        raise Exception("Resource not found.")

    base = "home/accept"

    url = '{}/{}?data={}'.format(base, token, urllib.quote_plus(json.dumps(invite.__dict__)))

    return redirect(url)

console:

>>> oi = OrgInvite.objects.get(token=100) 
>>> oi
<OrgInvite: OrgInvite object>
>>> oix = oi.__dict__
>>> oix
{'used': False, 'name': u'', '_state': <django.db.models.base.ModelState object at 0x10377a610>, 'email': u'', 'token': u'100', 'org_id_id': 101, 'account_id_id': 301, 'is_admin': False, 'id': 1}
>>> json.dumps(oix)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 244, in dumps
    return _default_encoder.encode(obj)
  File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <django.db.models.base.ModelState object at 0x10377a610> is not JSON serializable
like image 715
TJB Avatar asked Apr 11 '16 17:04

TJB


2 Answers

The __dict__ gives all the attributes of the instance, but you don't want all that extra baggage - for the purposes of serialization, you are only interested in the fields.

Your model does not contain anything special so the built-in helper function model_to_dict should be enough for your needs:

import json
from django.forms.models import model_to_dict

oi = OrgInvite.objects.get(token=100) 
oi_dict = model_to_dict(oi)
oi_serialized = json.dumps(oi_dict)

Your example was simple, only containing CharField, BooleanField, and ForeignKey all of which we can dump to json trivially.

For more complicated models, you might consider writing your own serializer. In this case, I recommend using the popular django-rest-framework which does all the work for you.

from rest_framework import serializers

class OrgInviteSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrgInvite
        fields = '__all__'
like image 154
wim Avatar answered Oct 04 '22 07:10

wim


If you do invite.__dict__, it's going to give you a dictionary of all data related to one invite object. However, the dict's values are not necessarily primitive types, but objects as well(ModelState is just one of them). Serializing that would not only not working because json doesn't accept python objects, but you could also serialize a lot of meta data that's not used.

Check out json official website to see what data types are json serializable. The fix would be either using django model serializer, or manually create a dict that in compliance to json format.

like image 36
Shang Wang Avatar answered Oct 04 '22 07:10

Shang Wang