Using Python simplejson to return pregenerated json
Asked Answered
S

2

5

I have a GeoDjango model object that I want't to serialize to json. I do this in my view:

lat = float(request.GET.get('lat'))
lng = float(request.GET.get('lng'))
a = Authority.objects.get(area__contains=Point(lng, lat))
if a:
    return HttpResponse(simplejson.dumps({'name': a.name, 
                                          'area': a.area.geojson,
                                          'id': a.id}), 
                        mimetype='application/json')

The problem is that simplejson considers the a.area.geojson as a simple string, even though it is beautiful pre-generated json. This is easily fixed in the client by eval()'ing the area-string, but I would like to do it proper. Can I tell simplejson that a particular string is already json and should be used as-is (and not returned as a simple string)? Or is there another workaround?

UPDATE Just to clarify, this is the json currently returned:

{
    "id": 95,
    "name": "Roskilde",
    "area": "{ \"type\": \"MultiPolygon\", \"coordinates\": [ [ [ [ 12.078701, 55.649927 ], ... ] ] ] }"
}

The challenge is to have "area" be a json dictionary instead of a simple string.

Syd answered 8/1, 2010 at 13:1 Comment(6)
"a particular string is already json and should be used as-is? " If that is the case then why are you doing simplejson send it as such, i dint understand what you mean by already json? You mean a dict?Orms
He means a.area.geojson is alread a json data say '{"lat": 221.22, "lng": 192.222}'. How can he avoid doing simplejson.dumps again?Danilodanio
I've tried to clarify the text, I realise it wasn't very clear.Syd
so, did you try simplejson.loads?Drillstock
I guess, since he don't want to do eval, he may not want to do .loads tooWinsome
I'd vote for simplejson.loads, it's the simplest and cleanest to do. Iff performance becomes a concern, can switch to cjson or do the string replacement thing...Seism
G
2

EDITED after author's edit:

Can you do something like this:

lat = float(request.GET.get('lat'))
lng = float(request.GET.get('lng'))
a = Authority.objects.get(area__contains=Point(lng, lat))
if a:
    json = simplejson.dumps({'name': a.name, 
                             'area': "{replaceme}",
                             'id': a.id}), 
    return HttpResponse(json.replace('"{replaceme}"', a.area.geojson),
                        mimetype='application/json')
Griffith answered 8/1, 2010 at 13:33 Comment(0)
E
5

I think the clean way to do this is by extending JSONEncoder, and creating an encoder that detects if the given object is already JSON. if it is - it just returns it. If its not, it uses the ordinary JSONEncoder to encode it.

class SkipJSONEncoder(simplejson.JSONEncoder):
     def default(self, obj):
         if isinstance(obj, str) and (obj[0]=='{') and (obj[-1]=='}'): 
             return obj
         return simplejson.JSONEncoder.default(self, obj)

and in your view, you use:

simplejson.dumps(..., cls=SkipJSONEncoder)

If you have a cleaner way to test that something is already JSON, please use it (my way - looking for strings that start in '{' and end in '}' is ugly).

Erickson answered 8/1, 2010 at 14:23 Comment(1)
Looks like a good solution, but what is implied in the first ...?(sorry, my Python is pretty poor)Syd
G
2

EDITED after author's edit:

Can you do something like this:

lat = float(request.GET.get('lat'))
lng = float(request.GET.get('lng'))
a = Authority.objects.get(area__contains=Point(lng, lat))
if a:
    json = simplejson.dumps({'name': a.name, 
                             'area': "{replaceme}",
                             'id': a.id}), 
    return HttpResponse(json.replace('"{replaceme}"', a.area.geojson),
                        mimetype='application/json')
Griffith answered 8/1, 2010 at 13:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.