Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get Django to return JsonResponse with no extra quotes or quote escapes?

Using Django 1.7, whenever I return the following bit of JsonResponse in a view:

from django.http import JsonResponse
token = "1$aEJUhbdpO3cNrXUmFgvuR2SkXTP9="
response = JsonResponse({"token": token})
return response

I'm getting the following HTTP response from web browser/cURL:

"{\"token\": \"1$aEJUhbdpO3cNrXUmFgvuR2SkXTP9=\"}"

What I want, and what I had in Django 1.3 was this:

{"token": "1$aEJUhbdpO3cNrXUmFgvuR2SkXTP9="}

I have two mobile apps in production that rely on a private API using Django, and unfortunately they are expecting the second kind of response, with no extra quotes (the quote that surround the entire JSON making it a string) and no quote escapes.

My question is, is there some built-in way to force Django response to not escape a JSON response?

I wrote the following middleware to do it, but...it seems like a really fragile brute force way of going about it:

class UnescapeJSON(object):
    def process_response(self, request, response):
        """
        Directly edit response object here, searching for and replacing terms
        in the html.
        """
        if re.search('^/api/.*', request.path):
            r = response.content
            r = r.replace('\\', '')
            r = r.lstrip('"')
            r = r.rstrip('"')
            response.content = r
        return response

So I'm hoping there is a smarter way.

Backstory is I'm trying to update an old legacy code base from Django 1.3 to 1.8. I currently have it on 1.7 in my local dev environment. Django 1.3 returned the JSON the correct way, without extra quotes and backslashes.

One nice thing about returning the JSON in this way:

{"token": "1$aEJUhbdpO3cNrXUmFgvuR2SkXTP9="}

...is that I'm using jQuery.post({success:...}) to handle this JSON response, and it automatically runs jQuery.parseJSON() for me, turning it into a JSON object I can access with dot notation.

I can't just fix string on the client side and re-run parseJSON() manually, because that would involve getting all my users to upgrade their mobile app.

So I have to get JSON formatted as above, or my mobile API is effectively broken.

One bit of info I should add. This API is using Django Piston :(. I'm using a 1.7x compatible version I found. It's not in the cards for me to swap in Django REST Framework right now. Believe me, I will as soon as feasibly possible.

like image 563
DomoDomo Avatar asked Jun 04 '15 22:06

DomoDomo


2 Answers

For those finding this thread on Google, ditch JsonResponse. If you try and use jsonp, you will get thrown the error

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

If you set safe to False you'll get the response sent back with quotes around it which will then fail on the client end. Instead

return HttpResponse(response_data, content_type='application/json')
like image 156
Phillip Avatar answered Nov 03 '22 01:11

Phillip


I was seeing this with Django REST Framework in a 1.8 deployment. I just went from 1.3 to 1.8 recently. However, I didn't have RESTful support until 1.8 so I can't claim this ever worked (or didn't work).

The problem was caused by my creating Python dictionaries, serializing them to JSON objects, and then calling json.dumps() on the result. The last step, calling json.dumps(), and passing the result to the Response() constructor is what led to a string that got wrapped with double quotes.

Changing my code from:

def get(self, request, year, month, day, format=None):
    resp = None
    date = datetime(int(year), int(month), int(day), 0, 0)
    clinics = Clinic.objects.all()
    for x in clinics:
        aDate = datetime(x.date.year, x.date.month, x.date.day)
        if date >= aDate and date <= aDate + timedelta(days=x.duration):
            resp = serializers.serialize("json", [x, ])
            struct = json.loads(resp)
            struct[0]["fields"]["id"] = x.id
            resp = json.dumps(struct[0]["fields"])
            break

    if resp == None:
        raise NotFound()
    return Response(resp)

to

def get(self, request, year, month, day, format=None):
    resp = None
    date = datetime(int(year), int(month), int(day), 0, 0)
    clinics = Clinic.objects.all()
    for x in clinics:
        aDate = datetime(x.date.year, x.date.month, x.date.day)
        if date >= aDate and date <= aDate + timedelta(days=x.duration):
            resp = serializers.serialize("json", [x, ])
            struct = json.loads(resp)
            struct[0]["fields"]["id"] = x.id
            resp = struct[0]["fields"]
            break

    if resp == None:
        raise NotFound()
    return Response(resp)

fixed the problem (for me) in Django REST. (Note, the line where I assign resp is the only one that needed changing). Hope this helps somehow.

like image 41
slogan621 Avatar answered Nov 03 '22 02:11

slogan621