I am trying to output a dictionary as JSON, using python 2.7 (this can not be upgraded)
The keys in the data
are strings that contain numbers, like 'item_10'
, and have an arbitrary order. For example, this code generates some test data:
import random
data = {}
numbers = list(range(1, 12))
random.shuffle(numbers)
for value in numbers:
data['item_{}'.format(value)] = 'data{}'.format(value)
I tried using:
print(json.dumps(data, sort_keys=True, indent=2))
However, I want the keys to be sorted naturally, like:
{
"item_1": "data1",
"item_2": "data2",
...
"item_10": "data10",
"item_11": "data11"
}
Instead, I get keys sorted by Python’s default sort order:
{
"item_1": "data1",
"item_10": "data10",
"item_11": "data11",
...
"item_2": "data2"
}
How can I get this result?
2
Answers
By making the keys "naturally comparable"
Supposing that we have a key function that implements the natural-sort comparison, as in Claudiu’s answer for the related question:
Then we can create a wrapper class for strings which is compared using that function, transform the keys of the dict, and proceed as before:
Note that
functools.total_ordering
is introduced in Python 3.2. In older versions, we should instead define__gt__
,__le__
and__ge__
explicitly, in corresponding ways. (Python’s sort algorithm should not use these, but it is a good idea to include consistent definitions for correctness.) Of course, the basestr
‘s implementations of__eq__
and__ne__
do not need to be replaced.(In 2.7 we could also instead implement a corresponding
__cmp__
, but this will break in 3.x.)By putting the keys in order first
In 3.7 and up, dictionary keys are guaranteed to preserve their order; in earlier versions, we can use
collections.OrderedDict
to get that property. Note that this does not sort keys, but maintains the order of insertion.Thus, we can determine the necessary order for keys, and create a new dict by inserting keys in that order:
Since the data was sorted ahead of time,
sort_keys=True
is no longer necessary. In modern versions, since we are using the built-indict
, we could also write a dict comprehension (rather than passing a generator to the constructor).Using
simplejson
instead of the standard libraryThe third-party simplejson library is the original basis of Python’s standard library JSON support; however, it is actively maintained by the original developer, and the standard library uses very old versions, relatively speaking. For example, in Python 3.8, the standard library appears to be based on simplejson 2.0.9; as of posting, the latest version of simplejson is 3.18.3.
Using an up-to-date version of
simplejson
, we can simply specify the sort key asitem_sort_key
: