Specyfikacja JSON formalnie nie określa kolejności kluczy w obiekcie — {"a": 1, "b": 2} i {"b": 2, "a": 1} to te same dane. W praktyce jednak kolejność kluczy często ma znaczenie — zarówno dla ludzi, jak i dla narzędzi pracujących z tymi danymi.
Po co sortować klucze
- Porównanie (diff). Jeśli dwa dokumenty JSON z identycznymi danymi mają różną kolejność kluczy, diff tekstowy pokaże fałszywą różnicę. Sortowanie eliminuje ten problem.
- Deterministyczny wynik. Jeśli ten sam obiekt jest serializowany wielokrotnie (np. do generowania hasza czy klucza cache), posortowane klucze gwarantują ten sam wynik za każdym razem.
- Czytelność. Posortowany alfabetycznie obiekt łatwiej szybko przejrzeć i znaleźć potrzebne pole, zwłaszcza w dużych strukturach.
Sortowanie rekurencyjne
Aby wynik był przewidywalny, sortowanie zwykle stosuje się rekurencyjnie — nie tylko do kluczy najwyższego poziomu, ale też do kluczy wszystkich zagnieżdżonych obiektów na dowolnej głębokości. Kolejność elementów w tablicach się przy tym nie zmienia, ponieważ dla tablic kolejność elementów jest znaczącą częścią danych.
Kiedy to niepotrzebne
Jeśli JSON jest odczytywany wyłącznie przez program (a nie przez człowieka czy narzędzie do diffów), sortowanie kluczy zwykle nie daje żadnej praktycznej korzyści — parsery odczytują obiekt tak samo niezależnie od kolejności pól.
Dlaczego ż i ź łatwo pomylić nawet przy poprawnym sortowaniu
Polski alfabet rozszerza łaciński o znaki ą, ć, ę, ł, ń, ó, ś, ź, ż, z których ł sortuje się zaraz po l, a nie razem z nim — to osobna litera w polskim porządku alfabetycznym, podobnie jak pozostałe znaki diakrytyczne mają swoje miejsce zaraz po literach bazowych. Kłopot w tym, że ź (U+017A) i ż (U+017C) wyglądają na pierwszy rzut oka niemal identycznie — różnią się tylko kropką nad kreską — więc nawet gdy sortowanie z uwzględnieniem polskiej lokalizacji ustawi klucze poprawnie, wzrokowa weryfikacja wyniku bywa zaskakująco trudna.
Sortowanie a lokalizacja
Alfabetyczne sortowanie kluczy zawierających nie tylko litery łacińskie zależy od tego, czy znaki są porównywane bajt po bajcie (Unicode code point), czy z uwzględnieniem reguł językowych (locale-aware collation). Na przykład porównanie bajtowe stawia wielkie litery przed małymi niezależnie od alfabetu, podczas gdy sortowanie z uwzględnieniem lokalizacji może uporządkować klucze inaczej.