2010-12-09 20 views
15

¿Hay alguna forma en Python 2.6 para suministrar una clave personalizada o una función cmp a sort_keys de JSON?pedido JSON sort_keys personalizado en Python

Tengo una lista de dicts procedentes de JSON de este modo:

[ 
    { 
    "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" 
    } 
] 

... lo que me he guardado en la variable de lista assigndb. Me gustaría poder cargar en el JSON, modificarlo y serializarlo de nuevo con dumps (o lo que sea), manteniendo intactos los pedidos de las claves .

Hasta ahora, he intentado algo como esto:

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) 

... pero por supuesto dumps no es compatible con una clave personalizada como list.sort() hace. Algo con una costumbre JSONEncoder tal vez? Parece que no puedo hacerlo funcionar.

Respuesta

4

Esto es un poco feo, pero en caso de que la solución de tokland no funciona para usted:

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) + "]" 
+0

Feo, pero funcional; ¡Gracias! –

12

Una idea (probado con 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}' 

Ver: OrderedDict + issue6105. El truco c_make_encoder parece ser solo necesario para Python 2.x. No es una solución directa porque tiene que cambiar dict s por OrderedDict s, pero aún puede ser utilizable. Revisé la biblioteca JSON (encode.py) y la ordenada está codificada:

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

Este trabajo _might_, pero los dicts vienen de JSON en un principio, por lo que las llaves ya no están ordenados. –

+0

Meh. OrderedDict es solo 2.7. –

+0

@Graham: independientemente de la versión de python, existen varios módulos que implementan dictados ordenados, elija cualquiera de ellos. – tokland

-1

Gracias. Necesitaba poner una clave de marca de tiempo: valor en la parte superior de mi objeto JSON sin importar qué. Obviamente, la clasificación de las teclas atornilla esto ya que comienza con "t".

Usando algo como esto, mientras que poner la clave de marca de tiempo en los dict_data inmediato trabajado:

d = collections.OrderedDict(dict_data) 
0

que tenían el mismo problema y collections.OrderedDict simplemente no era apto para la tarea, ya que todo lo ordena alfabéticamente. Así que escribí algo similar a la solución de 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)) 

Se utiliza la siguiente manera:

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' 
)) 
Cuestiones relacionadas