24
>>> 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 中将其捕获为异常,说在来自客户端的请求中发现了重复的键?

4

4 回答 4

37

application/json用于媒体类型的 rfc 4627建议使用唯一键,但并未明确禁止它们:

对象中的名称应该是唯一的。

来自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'
于 2013-02-15T20:10:18.597 回答
4

这是jfs 答案的 linter-fixed 和 type-annotated 版本。解决了各种 linter 突出显示的问题。它还针对 Python 3.6+ 进行了现代化改造,以使用 f-strings。

import json
from typing import Any, Dict, Hashable, List, Tuple

def raise_on_duplicate_keys(ordered_pairs: List[Tuple[Hashable, Any]]) -> Dict:
    """Raise ValueError if a duplicate key exists in provided ordered list of pairs, otherwise return a dict."""
    dict_out = {}
    for key, val in ordered_pairs:
        if key in dict_out:
            raise ValueError(f'Duplicate key: {key}')
        else:
            dict_out[key] = val
    return dict_out

json.loads('{"x": 1, "x": 2}', object_pairs_hook=raise_on_duplicate_keys)

ordered_pairs上面是一个元组列表,每个元组都有一个键和一个值。请参阅文档以获取object_pairs_hook.

于 2018-03-27T17:12:35.307 回答
1

或者,如果您想捕获所有重复键(每个级别),您可以使用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)
于 2013-02-15T21:26:55.003 回答
1

我根据此问题的其他用户发布的解决方案编写的一种替代方法是将这些重复项转换为数组:

def array_on_duplicate_keys(ordered_pairs):
    """Convert duplicate keys to arrays."""
    d = {}
    for k, v in ordered_pairs:
        if k in d:
            if type(d[k]) is list:
                d[k].append(v)
            else:
                d[k] = [d[k],v]
        else:
           d[k] = v
    return d

进而:

dict = json.loads('{"x": 1, "x": 2}', object_pairs_hook=array_on_duplicate_keys)

给你输出:

{'x': [1, 2]}

稍后,可以使用以下方法轻松检查条目的重复次数:

if type(dict['x']) is list:
    print('Non-unique entry in dict at x, found', len(dict['x']),'repetitions.')
于 2020-04-24T19:40:12.103 回答