43

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
  • 733
  • 1
  • 7
  • 19
  • 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
  • Try reviewing changes to a 30,000 line swagger.json API spec, then tell me that order is not important ;) We use the sorting features of https://github.com/thim81/openapi-format to take care of the problem. – Ed Randall Aug 07 '23 at 16:49

5 Answers5

98

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

app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False

Update: For Flask 2.3 and later use this:

app.json.sort_keys = False
Saeed Ir
  • 1,974
  • 2
  • 20
  • 22
  • 13
    thank you and that is absolutely insane that false is not the default – yigal Oct 22 '20 at 05:10
  • 1
    Awesome! Agree with @yigal that this should be set as default – Nam G VU Oct 20 '22 at 03:10
  • @yigal I thought so too, but the original commit where this was added gives a fairly good reason: ["Order JSON keys by default to avoid trashing HTTP caches"](https://github.com/pallets/flask/commit/77d293cf49e586f03fbea96d0bae237bc7ed230f) – Seb Apr 17 '23 at 06:35
  • 2
    Summary: For Flask pre-2.3, use `app.config['JSON_SORT_KEYS'] = False`. For Flask 2.3 and above, use `app.json.sort_keys = False`. – yfpb Aug 13 '23 at 17:18
  • any option for google cloud function. because there is no app – Md. Parvez Alam Aug 31 '23 at 09:05
15

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
  • 1,048,767
  • 296
  • 4,058
  • 3,343
11

The solution to set app.config['JSON_SORT_KEYS'] = False no longer does the trick in the latest version of Flask (2.3), nor does it in Flask's asyncio-based sibling Quart.

The new way to turn off sorting is to update the sort_keys attribute on the app's JSONProvider instance:

from flask import Flask  # or from quart import Quart, etc.

app = Flask(__name__)
app.json.sort_keys = False

or if you're already using a custom JSONProvider subclass:

from flask import Flask
from flask.json.provider import DefaultJSONProvider

class CustomJSONProvider(DefaultJSONProvider):
    sort_keys = False

app = Flask(__name__)
app.json = CustomJSONProvider(app)
Seb
  • 4,422
  • 14
  • 23
2

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
2

Please refer to the Flask documentation 2.3.x changes (Released: 2023-04-25)

The JSON_AS_ASCII, JSON_SORT_KEYS, JSONIFY_MIMETYPE, and JSONIFY_PRETTYPRINT_REGULAR config keys are removed.

Version 2.3.x has DefaultJSONProvider class with a default parameter: sort_keys = True

DefaultJSONProvider Class description

To disable default JSON (dict) key sort:

from flask import Flask

app = Flask(__name__)
app.json.sort_keys = False
Shayan
  • 1,931
  • 1
  • 9
  • 14