2013-02-15 11 views
11
>>> raw_post_data = request.raw_post_data 
>>> print raw_post_data 
{"group":{"groupId":"2", "groupName":"GroupName"}, "members":{"1":{"firstName":"fName","lastName":"LName","address":"address"},"1": {"firstName":"f_Name","lastName":"L_Name","address":"_address"}}} 
>>> create_request = json.loads(raw_post_data) 
>>> print create_request 
{u'group': {u'groupName': u'GroupName', u'groupId': u'2'}, u'members': {u'1': {u'lastName': u'L_Name', u'firstName': u'f_Name', u'address': u'_address'}}} 

Jak widać członków z kluczem „1” jest nadpisywany podczas korzystania json.dumps()json.loads pozwala zduplikowane klucze w słowniku, zastępując pierwszą wartość

Czy istnieje jakiś sposób, aby złapać go jako wyjątku python, mówiąc znalezionych duplikatów kluczy na żądanie od klienta?

+1

related: [obsługę simplejson z samych nazwach jednostek] (http: // stackoverflow .pl/questions/7825261/simplejson-handling-of-same-named-entities) – jfs

Odpowiedz

22

The rfc 4627 for application/json media type zaleca unikatowych kluczy, ale nie zabraniajcie im wyraźnie:

Nazwy wewnątrz obiektu powinna być unikalna.

Od rfc 2119:

POWINIEN Słowo to lub przymiotnik "Recommended", oznacza, że ​​nie
mogą istnieć uzasadnione powody w szczególnych okolicznościach ignorować
szczególną pozycję, ale Pełne konsekwencje muszą być zrozumiane i ostrożnie zważone przed wyborem innego kursu.

import json 

def dict_raise_on_duplicates(ordered_pairs): 
    """Reject duplicate keys.""" 
    d = {} 
    for k, v in ordered_pairs: 
     if k in d: 
      raise ValueError("duplicate key: %r" % (k,)) 
     else: 
      d[k] = v 
    return d 

json.loads(raw_post_data, object_pairs_hook=dict_raise_on_duplicates) 
# -> ValueError: duplicate key: u'1' 
+0

+1 Cóż, wygląda o wiele lepiej :) – root

+0

Tak, tego, czego szukałem .. Tx .. Jednak, json.load() biblioteka powinna zapewnić coś, co może zrobić podobnie. –

+2

@AnujAcharya: Problem polega na tym, że są dobre wykorzystywać przypadki dla zwykłego dyktatu, "multidict", "multi-only-on-dups-dict", "raise-on-dups-dict" (z ValueError lub KeyError?) i być może inne. I chcesz dokładnie to samo w 'json.loads' i' json.load' oraz 'csv.DictReader' i' yaml.load', i tak dalej. (Zobacz aktualną dyskusję na temat python-pomysłów na temat 'csv'.) Nie chcesz pisać wszystkich możliwych zachowań dla wszystkich możliwych funkcji ładowania. I 'object_pairs_hook' wydaje się być właściwym sposobem rozłączenia go. – abarnert

1

Alternatywnie, jeśli chcesz złapać wszystkie zduplikowane klucze (na poziomie) można użyć collections.Counter

from collections import Counter 

class KeyWatcher(dict): 

    def __init__(self, *args): 
     duplicates = [d for d,i in Counter([pair[0] for pair in args[0]]).items() if i > 0] 
     if duplicates: 
      raise KeyError("Can't add duplicate keys {} to a json message".format(duplicates)) 
     self.update(*args[0]) 

json.loads(raw_post_data, object_pairs_hook=KeyWatcher) 
+1

Twój licznik liczy liczbę wystąpień, więc aby znaleźć klucze, które pojawiają się więcej niż jeden raz (tj. Duplikaty), warunkiem na liście powinno być 'if i> 1', a nie' if i> 0'. –

+0

Faktycznie, nawet po tej korekcie nadal nie działa tak, jak reklamowano. Jednak kod J.F. Sebastiana działał. Polecam jej używanie, nawet jeśli wydaje się, że w tym podejściu jest jakaś elegancja, ponieważ wykorzystuje ona listy zamiast pętli. –

+0

Szybką poprawką byłoby "self.update (args [0])", tj. Bez gwiazdki. KeyWatcher jest wywoływany za pomocą tylko jednego argumentu, więc '* args' nie jest w ogóle pomocne. – VPfB

Powiązane problemy