【问题标题】:json.loads allows duplicate keys in a dictionary, overwriting the first valuejson.loads 允许字典中的重复键,覆盖第一个值
【发布时间】:2013-02-15 19:52:40
【问题描述】:
>>> 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'}}}

正如您所见,当我使用 json.dumps() 时,具有键 '1' 的成员被覆盖

有没有办法在python中将其捕获为异常,说在来自客户端的请求中发现重复键?

【问题讨论】:

标签: python json


【解决方案1】:

The rfc 4627 for application/json media type 建议使用唯一键,但并未明确禁止:

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

来自rfc 2119

应该这个词,或形容词“推荐”,意味着那里
在特定情况下可能存在忽略a的正当理由
特定项目,但必须了解全部含义并
在选择不同的课程之前仔细权衡。

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'

【讨论】:

  • 是的,我正在寻找的东西..Thx.. 但是,json.load() 库应该提供可以做类似的事情..
  • @AnujAcharya:问题是普通字典、“multidict”、“multi-only-on-dups-dict”、“raise-on-dups”都有很好的用例-dict"(带有 ValueError 或 KeyError?),可能还有其他。你想要在json.loadsjson.loadcsv.DictReaderyaml.load 中得到完全相同的东西,等等。 (请参阅当前关于csv 的python-ideas 讨论。)您不想为所有可能的加载函数编写所有可能的行为。而object_pairs_hook 似乎正是解耦它的正确方法。
  • 我真的不明白什么是ordered_pa​​ir和raw_post_data。你能解释一下你的参数吗?几乎每个网页我都看到其他人复制粘贴的解决方案。没有解释。由于我是 python 新手,我需要了解更多信息。
  • @limonik 见json.loads docsraw_post_data 是来自问题的 json 文本。 ordered_pairs 是一个 (key, value) 可迭代对象,对应一个 json 对象,通常被解析成 Python dict
【解决方案2】:

这是answer by 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 的文档。

【讨论】:

    【解决方案3】:

    或者,如果您想捕获所有重复键(每个级别),您可以使用 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)
    

    【讨论】:

    • 您的计数器正在计算出现次数,因此要查找出现多次(即重复)的键,列表理解中的条件应为 if i > 1 而不是 if i > 0
    • 实际上,即使经过更正,它似乎仍然不像宣传的那样工作。不过,J.F. Sebastian 的代码有效。我建议使用它,即使这种方法看起来有些优雅,因为它使用列表推导而不是循环。
    • 快速解决方法是self.update(args[0]),即不带星号。仅使用一个参数调用 KeyWatcher,因此 *args 根本没有帮助。
    【解决方案4】:

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

    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.')
    

    【讨论】:

      猜你喜欢
      • 2023-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多