Question l'ordre de tri_keys JSON personnalisé en Python


Existe-t-il un moyen, dans Python 2.6, de fournir une clé personnalisée ou une fonction cmp aux clés de tri JSON?

J'ai une liste de dicts venant de JSON comme ça:

[
  {
    "key": "numberpuzzles1",
    "url": "number-puzzle-i.html",
    "title": "Number Puzzle I",
    "category": "nestedloops",
    "points": "60",
    "n": "087"
  },
  {
     "key": "gettingindividualdigits",
     "url": "getting-individual-digits.html",
     "title": "Getting Individual Digits",
     "category": "nestedloops",
     "points": "80",
     "n": "088"
  }
]

... que j'ai stocké dans la variable liste assigndb. Je voudrais pouvoir charger le JSON, le modifier et le sérialiser avec dumps (ou autre), en gardant la commandes des clés intactes.

Jusqu'à présent, j'ai essayé quelque chose comme ça:

ordering = {'key': 0, 'url': 1, 'title': 2, 'category': 3,
             'flags': 4, 'points': 5, 'n': 6}

def key_func(k):
    return ordering[k]

# renumber assignments sequentially
for (i, a) in enumerate(assigndb):
    a["n"] = "%03d" % (i+1)

s = json.dumps(assigndb, indent=2, sort_keys=True, key=key_func)

...mais bien sûr dumps ne supporte pas une clé personnalisée comme list.sort() Est-ce que. Quelque chose avec une coutume JSONEncoder peut être? Je ne peux pas sembler aller de l'avant.


15
2017-12-09 19:54


origine


Réponses:


C'est plutôt moche, mais si la solution de tokland ne fonctionne pas pour vous:

data = [{'category': 'nestedloops', 'title': 'Number Puzzle I', 'url': 'number-puzzle-i.html', 'n': '087', 'points': '60', 'key': 'numberpuzzles1'}, {'category': 'nestedloops', 'title': 'Getting Individual Digits', 'url': 'getting-individual-digits.html', 'n': '088', 'points': '80', 'key': 'gettingindividualdigits'}]
ordering = {'key': 0, 'url': 1, 'title': 2, 'category': 3,
            'flags': 4, 'points': 5, 'n': 6}
outlist = []
for d in data:
    outlist.append([])
    for k in sorted(d.keys(), key=lambda k: ordering[k]):
        outlist[-1].append(json.dumps({k: d[k]}))

for i, l in enumerate(outlist):
    outlist[i] = "{" + ",".join((s[1:-1] for s in outlist[i])) + "}"

s = "[" + ",".join(outlist) + "]"

4
2017-12-09 20:55



Une idée (testée avec 2.7):

import json
import collections
json.encoder.c_make_encoder = None
d = collections.OrderedDict([("b", 2), ("a", 1)])
json.dumps(d)
# '{"b": 2, "a": 1}'

Voir: OrderedDict + issue6105. le c_make_encoder le piratage ne semble être nécessaire que pour Python 2.x. Pas une solution directe parce que vous devez changer dicts pour OrderedDicts, mais il peut être encore utilisable. J'ai vérifié la bibliothèque json (encode.py) et la commande est codée en dur:

if _sort_keys:
    items = sorted(dct.items(), key=lambda kv: kv[0])

12
2017-12-09 20:28



J'ai eu le même problème et collections.OrderedDict était tout simplement pas à la hauteur de la tâche car elle ordonnait tout par ordre alphabétique. J'ai donc écrit quelque chose de similaire à la solution d'Andrew Clark:

def json_dumps_sorted(data, **kwargs):
    sorted_keys = kwargs.get('sorted_keys', tuple())
    if not sorted_keys:
        return json.dumps(data)
    else:
        out_list = []
        for element in data:
            element_list = []
            for key in sorted_keys:
                if key in element:
                    element_list.append(json.dumps({key: element[key]}))
            out_list.append('{{{}}}'.format(','.join((s[1:-1] for s in element_list))))
        return '[{}]'.format(','.join(out_list))

Vous l'utilisez comme ceci:

json_string = json_dumps_sorted([
    {
        "key": "numberpuzzles1",
        "url": "number-puzzle-i.html",
        "title": "Number Puzzle I",
        "category": "nestedloops",
        "points": "60",
        "n": "087"
    }, {
        "key": "gettingindividualdigits",
        "url": "getting-individual-digits.html",
        "title": "Getting Individual Digits",
        "category": "nestedloops",
        "points": "80",
        "n": "088"
    }
], sorted_keys=(
    'key', 
    'url', 
    'title', 
    'category',
    'flags', 
    'points', 
    'n'
))

0
2017-10-25 09:50



Merci. Je devais mettre une clé d'horodatage: valeur en haut de mon objet JSON, peu importe quoi. De toute évidence, le tri des clés a foiré cela car il commence par "t".

En utilisant quelque chose comme ça, en mettant la clé d'horodatage dans le dict_data a tout de suite travaillé:

d = collections.OrderedDict(dict_data)

-1
2017-12-03 21:39