Json.loads позволяет дублировать ключи в словаре, переписывая первое значение

>>> 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'}}}

Как вы видите, члены с ключом "1" перезаписываются, когда я использую json.dumps()

Есть ли способ уловить его как исключение в python, заявив, что найденные дубликаты ключей в запросе от клиента?

Ответ 1

Формат rfc 4627 для application/json рекомендует уникальные ключи, но он не запрещает их явно:

Имена внутри объекта ДОЛЖНЫ быть уникальными.

Из rfc 2119:

ДОЛЖНО. Это слово или прилагательное "РЕКОМЕНДУЕТСЯ" означает, что там могут существовать обоснованные причины в особых обстоятельствах, чтобы игнорировать конкретный элемент, но все последствия должны быть поняты и
тщательно взвешивается перед выбором другого курса.

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'

Ответ 2

Альтернативно, если вы хотите поймать все дубликаты ключей (за уровень), вы можете использовать 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)