32

I'm trying to pass sorted dictionary to jsonify() function and then use it from within JS code to take out values. What I see there is that even though I pass correct values, they are reordered by jsonify for some reason.


json_data = {
    "11": {
        "j_id": "out",
    },
    "aa": {
        "j_id": "in",
    },
    "bb": {
        "j_id": "out",
    },
}

jkeys=json_data.keys()
sorted_json = sorted(jkeys, key=lambda x: json_data[x]['j_id'], reverse=False)

new_sorted=OrderedDict()
for rec in sorted_json:
    new_sorted[rec]=json_data[rec]

print('sort dict: {}'.format(new_sorted))

The output is correct and I can see proper values which in my case should be: aa, 11, bb

>>> from collections import OrderedDict
>>>
>>> json_data = {
...     "11": {
...         "j_id": "out",
...     },
...     "aa": {
...         "j_id": "in",
...     },
...     "bb": {
...         "j_id": "out",
...     },
... }
>>>
>>> jkeys=json_data.keys()
>>> sorted_json = sorted(jkeys, key=lambda x: json_data[x]['j_id'], reverse=False)
>>>
>>> new_sorted=OrderedDict()
>>> for rec in sorted_json:
...     new_sorted[rec]=json_data[rec]
...
>>> print('sort dict: {}'.format(new_sorted))
sort dict: OrderedDict([('aa', {'j_id': 'in'}), ('11', {'j_id': 'out'}), ('bb', {'j_id': 'out'})])

Unfortunately, when I pass this to jsonify() function and then for example console.log() output of flask data, the orderd becomes like that: 11, aa, bb. Additionally to that, I've done some research and found this stackoverflow answer, leading to some good documentation notes which clearly states that setting JSON_SORT_KEYS to False is not recommended way. Then I checked this github issue and it seems that problem is not fully resolved in flask.

What would be the best way to fix it in my case?

zerocool
  • 595
  • 1
  • 5
  • 18
  • 3
    JSON objects are *not ordered structures*. If you need to give things a specific order, use a list. – Martijn Pieters Jan 30 '19 at 17:20
  • 2
    Serialising an `OrderedDict` to JSON produces a JSON document with the order preserved, but that doesn't mean that a Javascript client that decodes from JSON again won't drop your ordering. You *have* to use `app.config["JSON_SORT_KEYS"] = False` if you feel specifying the order is important anyway. – Martijn Pieters Jan 30 '19 at 17:21
  • There really is no "problem" here. JSON objects have no order. You shouldn't write code that relies on any particular order of data serialized to JSON objects. If you need order, use a JSON array. – juanpa.arrivillaga Jan 30 '19 at 17:26

3 Answers3

70

Add this config line to your code after the app definition:

app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
Saeed Ir
  • 1,498
  • 2
  • 16
  • 20
14

JSON objects are unordered structures, and your browser could easily end up discarding the order of your JSON keys again.

From the JSON standard:

An object is an unordered set of name/value pairs.

Bold emphasis mine. To remain standards compliant, use a list (JSON array) to capture a specific order.

That said, Flask can be made to preserve the order you set with OrderedDict.

  • Disable sorting app-wide, with JSON_SORT_KEYS = False.

    With this setting at the default True, jsonify() sorts keys to provide stable HTTP responses that are cacheable. The documentation warns against disabling this only to make you aware of the downside of setting this to False.

    However, if you are using Python 3.6 or newer this concern doesn't actually play because as of that version the built-in dict type also preserves insertion order, and so there is no problem with the order changing from one Python run to the next.

  • Instead of using jsonify(), use flask.json.dumps() directly, and create your own Response object. Pass in sort_keys=False:

    from flask import json
    
    response = current_app.response_class(
        json.dumps(new_sorted, sort_keys=False),
        mimetype=current_app.config['JSONIFY_MIMETYPE'])
    
Martijn Pieters
  • 963,270
  • 265
  • 3,804
  • 3,187
1

By default, keys from json are sorted to true. You need to override this in your project config as follows:

app.config['JSON_SORT_KEYS'] = False