Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maintain order when dumping dict to JSON

Tags:

python

json

flask

I want to serialize the results of a database query to JSON. Each row has a number of columns, with I add to a dict. The columns are in a certain order, but when I serialize the data the order changes. I tried using an OrderedDict, but still saw the same issue. How can I maintain the order of the columns?

res = {'a': i[0], 'b': i[1], 'c': i[2]}
return jsonify(res=res)
res = OrderedDict()
res['a'] = i[0]
res['b'] = i[1]
res['c'] = i[3]
return jsonify(res=res)
like image 853
90abyss Avatar asked Nov 04 '15 17:11

90abyss


People also ask

Does JSON dump change order?

JSON usually doesn't care about the order of the items. Therefore, when we dump a Python dictionary to a JSON string, the order of the items will be kept as-is. However, if we do want to sort the converted JSON string by the item keys, we can easily set sort_keys parameter to True .

Does JSON loads preserve order?

loads() doesn't keep order [duplicate] Bookmark this question. Show activity on this post.

Does dictionary preserve order?

A dictionary is a collection which is unordered, mutable, and indexed. In Python, dictionaries are written with curly brackets {}, and they have key:value pair. Updated in Python version 3.7. Dictionaries preserve insertion order.

How is the ordered dict preserving order?

Python's OrderedDict is a dict subclass that preserves the order in which key-value pairs, commonly known as items, are inserted into the dictionary. When you iterate over an OrderedDict object, items are traversed in the original order. If you update the value of an existing key, then the order remains unchanged.


2 Answers

Python dicts and JSON objects have no order, so there is no direct way to do this. In Python, OrderedDict maintains the insert order, but there is no equivalent in JSON. You can convert the dict to a list of its items, since lists are ordered.

data = OrderedDict()
data['a'] = 1
data['b'] = 2
jsonify(data=list(data.items()))

If you need to know that this particular list is actually an ordered dict, you can use a tagging scheme to tag the type of each value. This gets considerably more complicated. You can find an example of it in Flask's session serializer.

like image 59
davidism Avatar answered Oct 28 '22 13:10

davidism


It's not possible using Flask's jsonify(), but it is possible using just plain vanilla json.dumps(). The only catch is that you have to get the content length to match, since this is set in the response headers:

import json
from collections import OrderedDict
from flask import Flask, jsonify
app = Flask(__name__)

@app.route("/unordered")
def unordered_dict():
    d = OrderedDict([('e',1),('d',2),('c',3),('b',4),('a',5)])
    output = jsonify(d)
    return output

@app.route("/ordered")
def ordered_dict():
    d = OrderedDict([('e',1),('d',2),('c',3),('b',4),('a',5)])
    response = json.dumps(d)
    placeholder = '_'*(len(response)-2) # Create a placeholder, -2 due to ""
    output = jsonify(placeholder) # This needs to be the same length as the response
    output.response[0] = response + '\n' # Replace with the actual response
    return output

The first will return by default in alphabetical key order, or random order if app.config['JSON_SORT_KEYS'] = False. The second should return the keys in the order specified.

Caution: As the others noted, the JSON specification doesn't require order. However, it seems most browsers respect the order anyway, so this can be used in cases where order would be nice but not critical. If order is critical, then your only option is to convert to a list before converting to JSON.

(Note: prior to https://github.com/pallets/flask/issues/1877, the Content-Length wasn't explicitly set, so it was much simpler!)

like image 26
Cliff Kerr Avatar answered Oct 28 '22 14:10

Cliff Kerr