16

I searched on Google and found an answer but it's not working for me. I have to send a list as JsonResponse in Django, similar to this:

list_to_json =[{"title": "hello there",
                "link": "www.domain.com",
                "date":   ...},
               {},{},{},...]

I am converting this to JSON by applying StackOverflow question1 and question2 but it's not working for me. I get the following error:

In order to allow non-dict objects to be serialized set the safe parameter to False

Here's my code:

    def json_response(request):
        list_to_json=[{"title": ..., "link": ..., "date": ...},{...}]
        return JsonResponse(json.dumps(list_to_json) )
Community
  • 1
  • 1
shuboy2014
  • 1,350
  • 2
  • 18
  • 44
  • 1
    Does this solve your problem: `return JsonResponse(json.dumps(list_to_json), safe=False)`? – jape Jun 19 '16 at 17:20
  • The python dict in your sample's second line is invalid. Please also include the full stacktrace. Otherwise hard to follow IMO – Dilettant Jun 19 '16 at 17:21
  • 2
    The whole point of a `JsonResponse` is that it will serialize the data for you. Calling `json.dumps()` on the data will encode it twice. – knbk Jun 19 '16 at 17:32

4 Answers4

38
return JsonResponse(list_to_json, safe=False)

Take a look at the documentation:

The safe boolean parameter defaults to True. If it’s set to False, any object can be passed for serialization (otherwise only dict instances are allowed). If safe is True and a non-dict object is passed as the first argument, a TypeError will be raised.

Adam Hopkins
  • 6,837
  • 6
  • 32
  • 52
4

Adding this answer for anyone wondering why this isn't "safe" by default. Packing a non-dict data structure into a response makes the service vulnerable to a pre-ES5 JSON Hijacking attack.

Basically, with the JSONResponse you're using here, if a user is authenticated to your site, he can now retrieve that list of {title, link, date} objects and that's fine. However, an attacker could include that endpoint as a script source on his own malicious page (cross site script inclusion, aka XSSI):

<script src="https://www.yourwebsite.com/secretlinks/"></script>

Then, if an unsuspecting authenticated user navigates to the malicious page, the browser will unknowingly request the array of data from your site. Since your service is just returning an unassigned array, the attacker must also poison the js Array constructor (this is the part of the attack that was fixed in ES5). Before ES5, the attacker could simply override the Array constructor like so:

Array = function() {secret = this;}

Now secret contains your list of dictionaries, and is available to the rest of the attacker's script, where he can send it off to his own server. ES5 fixed this by forcing the use of brackets to be evaluated by the default Array constructor.

Why wasn't this ever an issue for dictionary objects? Simply because curly brackets in javascript denote an isolated scope, and so there's no way for the attacker to inject his own code into the scope created by the returned dictionary which is surrounded by curly brackets.

More info here: https://security.stackexchange.com/questions/159609/how-is-it-possible-to-poison-javascript-array-constructor-and-how-does-ecmascrip?newreg=c70030debbca44248f54cec4cdf761bb

stackPusher
  • 6,076
  • 3
  • 35
  • 36
2

You have do include serializers or you can do this by using safe= False to your response data.
Like

return JsonResponse(list_to_json, safe=False)
Projesh Bhoumik
  • 1,058
  • 14
  • 17
0

This is not a valid dictionary:

{"title": , "link" : , "date": }

because the values are missing. If you try adding the missing values instead, it works fine:

>>> json.dumps([{"title": "hello there", "link": "www.domain.com", "date": 2016}, {}])
'[{"link": "www.domain.com", "date": 2016, "title": "hello there"}, {}]'
VHarisop
  • 2,816
  • 1
  • 14
  • 28