I'm writing an API using Bottle, which so far has been fantastic. However, I've run up against a small hurdle when trying to return a JSON array. Here's my test app code:
from bottle import route, run @route('/single') def returnsingle(): return { "id": 1, "name": "Test Item 1" } @route('/containsarray') def returncontainsarray(): return { "items": [{ "id": 1, "name": "Test Item 1" }, { "id": 2, "name": "Test Item 2" }] } @route('/array') def returnarray(): return [{ "id": 1, "name": "Test Item 1" }, { "id": 2, "name": "Test Item 2" }] run(host='localhost', port=8080, debug=True, reloader=True)
When I run this and request each route, I get the JSON responses I'd expect from the first two routes:
/single
{ id: 1, name: "Test Item 1" }
/containsarray
{ "items": [ { "id": 1, "name": "Test Item 1" }, { "id": 2, "name": "Test Item 2" } ] }
So, I had expected returning a list of dictionaries to create the following JSON response:
[ { "id": 1, "name": "Test Object 1" }, { "id": 2, "name": "Test Object 2" } ]
But requesting the /array
route just results in an error. What am I doing wrong, and how can I return a JSON array in this manner?
Arrays in JSON are almost the same as arrays in JavaScript. In JSON, array values must be of type string, number, object, array, boolean or null. In JavaScript, array values can be all of the above, plus any other valid JavaScript expression, including functions, dates, and undefined.
Bottle's JSON plugin expects only dicts to be returned - not arrays. There are vulnerabilities associated with returning JSON arrays - see for example this post about JSON hijacking.
If you really need to do this, it can be done, e.g.
@route('/array') def returnarray(): from bottle import response from json import dumps rv = [{ "id": 1, "name": "Test Item 1" }, { "id": 2, "name": "Test Item 2" }] response.content_type = 'application/json' return dumps(rv)
According to Bottle's 0.12 documentation:
As mentioned above, Python dictionaries (or subclasses thereof) are automatically transformed into JSON strings and returned to the browser with the Content-Type header set to application/json. This makes it easy to implement json-based APIs. Data formats other than json are supported too. See the tutorial-output-filter to learn more.
Which means you don't need to import json
nor setting the content_type attribute of the response.
Thus, the code gets hugely reduced:
@route('/array') def returnarray(): rv = [{ "id": 1, "name": "Test Item 1" }, { "id": 2, "name": "Test Item 2" }] return dict(data=rv)
And the JSON document returned by the Web server would look like:
{"data": [{"id": 1, "name": "Test Item 1"}, {"id": 2, "name": "Test Item 2"}]}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With