从JSON序列化中排除空/ null值

31

我正在使用Python和simplejson将多个嵌套字典序列化为JSON。

是否有一种自动排除空/ null值的方法?

例如,对此进行序列化:

 {
     "dict1" : {
     "key1" : "value1",
     "key2" : None
     }
 }
 {
     "dict1" : {
     "key1" : "value1"
     }
 }

在Java中使用Jackson时,您可以使用Inclusion.NON_NULL来实现此目的。是否有一个simplejson的等价物?

9个回答

30
def del_none(d):
    """
    Delete keys with the value ``None`` in a dictionary, recursively.

    This alters the input so you may wish to ``copy`` the dict first.
    """
    # For Python 3, write `list(d.items())`; `d.items()` won’t work
    # For Python 2, write `d.items()`; `d.iteritems()` won’t work
    for key, value in list(d.items()):
        if value is None:
            del d[key]
        elif isinstance(value, dict):
            del_none(value)
    return d  # For convenience

使用示例:

>>> mydict = {'dict1': {'key1': 'value1', 'key2': None}}
>>> print(del_none(mydict.copy()))
{'dict1': {'key1': 'value1'}}

然后,你可以把它喂给json

在Python 3.5中会产生RuntimeError: dictionary changed size during iteration的错误。 - Aleksandr Panzin
""" Delete keys with the value ``None`` and empty string in a dictionary, recursively. This does not alter input, but copies the dictionary. Child dictionaries are also copied. No other objects are copied. """ rez = d.copy() for key, value in d.items(): if value is None or value == '': del rez[key] elif isinstance(value, dict): rez[key] = del_none(value) return rez``` - Aleksandr Panzin
1
@AleksandrPanzin:我已经更新了代码,以针对Python 3,并附有Python 2的注释。这是七年前写的!但是,我将其作为原地修改留下了。 - Chris Morgan
3
注意……这些答案中没有递归到列表。出现在列表中的字典将继续序列化其中出现的任何空值。 - Greg Ball
这仍然不是Python标准库的一部分吗?让我感到惊讶! - The Trav
显示剩余2条评论

24

我的Python3版本不同之处在于不改变输入,同时还可以递归处理嵌套在列表中的字典:

def clean_nones(value):
    """
    Recursively remove all None values from dictionaries and lists, and returns
    the result as a new dictionary or list.
    """
    if isinstance(value, list):
        return [clean_nones(x) for x in value if x is not None]
    elif isinstance(value, dict):
        return {
            key: clean_nones(val)
            for key, val in value.items()
            if val is not None
        }
    else:
        return value
例如:
a = {
    "a": None,
    "b": "notNone",
    "c": ["hello", None, "goodbye"],
    "d": [
        {
            "a": "notNone",
            "b": None,
            "c": ["hello", None, "goodbye"],
        },
        {
            "a": "notNone",
            "b": None,
            "c": ["hello", None, "goodbye"],
        }
    ]
}


print(clean_nones(a))

导致出现如下结果:

{
    'b': 'notNone',
    'c': ['hello', 'goodbye'],
    'd': [
        {
            'a': 'notNone',
            'c': ['hello', 'goodbye']
        },
        {
            'a': 'notNone',
            'c': ['hello', 'goodbye']
        }
    ]
}

2
这应该是被接受的答案,因为问题是关于JSON的,而JSON允许在列表中使用对象! - Radio Controlled

12
>>> def cleandict(d):
...     if not isinstance(d, dict):
...         return d
...     return dict((k,cleandict(v)) for k,v in d.iteritems() if v is not None)
... 
>>> mydict = dict(dict1=dict(key1='value1', key2=None))
>>> print cleandict(mydict)
{'dict1': {'key1': 'value1'}}
>>> 

一般情况下,我不喜欢使用 del,因为修改现有的字典可能会对其创建方式产生微妙的影响。而创建一个移除了 None 的新字典可以避免所有副作用。


1
在Python 3.x中,d.items() - wikier
只需将d.items()更改为Python3中的工作方式,只要您的结构不包含列表。 - Chris Mungall

2
这个解决方案是对@eric的上面的进行更正,它不能正确处理list类型。

规范JSON字典中的可以是以下3种类型之一:

  • dictionary
  • list
  • 值类型(stringintegerfloating point

注意:假设我们在处理真正只包含上述类型的规范JSON字典。如果字典包含其他类型(例如元组、自定义类等),则此解决方案将无法按预期工作。

这个解决方案(下面)与@eric的原始解决方案的本质区别在于,list可以包含dictionary类型的元素,其中我们想要删除具有None值的元素。

def cleandict(d):
    if isinstance(d, dict):
        return {k: cleandict(v) for k, v in d.items() if v is not None}
    elif isinstance(d, list):
        return [cleandict(v) for v in d]
    else:
        return d

注意:请记住我们不能从列表中删除None元素,因为这会影响列表数据的结构完整性。如果一些(或全部)列表元素具有None值,则它们应该按原样列在列表结构中,以保留列表的原始结构含义/完整性。

2
你可以尝试这种方法。在我的情况下(我使用 Python 3),它很有效。
def to_json(self):
    return json.dumps(self,
                      default=lambda o: dict((key, value) for key, value in o.__dict__.items() if value),
                      indent=4,
                      allow_nan=False)

0

如果'URL'在某个地方有价值,您能否保留它,而在另一个地方没有价值时将其删除?

'inline_keyboard': [
        [
            {'text': '0-0', 'url': 'someValue', 'login_url': None, 'callback_data': '0-0', 'switch_inline_query': None},
            {'text': '0-1', 'url': None, 'login_url': None, 'callback_data': '0-1', 'switch_inline_query': None}
        ],
        [
            {'text': '1-0', 'url': None, 'login_url': None, 'callback_data': '1-0', 'switch_inline_query': None},
            {'text': '1-1', 'url': None, 'login_url': None, 'callback_data': '1-1', 'switch_inline_query': None}
        ],
        [
            {'text': '2-0', 'url': None, 'login_url': None, 'callback_data': '2-0', 'switch_inline_query': None}
        ]
]

0
def excludeNone(d):
    for k in list(d):
        if k in d:
            if type(d[k]) == dict:
                excludeNone(d[k])
            if not d[k]:
                del d[k]

6
最好使用if d[k] is not None而不是if not d[k],否则空字符串和0值也将从输出中排除。 - Jacek Konieczny

0

这对我有效:

当字典具有dict/list/tuple值时...

例如,这是我的对象:

dict_obj = {
    'inline_keyboard': [
        [
            {'text': '0-0', 'url': None, 'login_url': None, 'callback_data': '0-0', 'switch_inline_query': None},
            {'text': '0-1', 'url': None, 'login_url': None, 'callback_data': '0-1', 'switch_inline_query': None}
        ],
        [
            {'text': '1-0', 'url': None, 'login_url': None, 'callback_data': '1-0', 'switch_inline_query': None},
            {'text': '1-1', 'url': None, 'login_url': None, 'callback_data': '1-1', 'switch_inline_query': None}
        ],
        [
            {'text': '2-0', 'url': None, 'login_url': None, 'callback_data': '2-0', 'switch_inline_query': None}
        ]
    ]
}

我写了这个函数:

def delete_none_values(obj):
    if isinstance(obj, dict):
        for k, v in list(obj.items()):
            if v is None:
                del obj[k]
            elif isinstance(v, dict):
                delete_none_values(v)
            elif isinstance(v, (list, tuple)):
                for _ in v:
                    delete_none_values(_)
    elif isinstance(obj, (list, tuple)):
        for _ in obj:
            delete_none_values(_)
    return obj

然后当使用这个函数时:

from json import dumps

print(
    dumps(
        delete_none_values(dict_obj.copy()),
        indent=2
    )
)

输出为:

{
  "inline_keyboard": [
    [
      {"text": "0-0", "callback_data": "0-0"},
      {"text": "0-1", "callback_data": "0-1"}
    ],
    [
      {"text": "1-0", "callback_data": "1-0"},
      {"text": "1-1", "callback_data": "1-1"}
    ],
    [
      {"text": "2-0", "callback_data": "2-0"}
    ]
  ]
}

0

我使用了MatanRubin函数并对其进行扩展,使其还可以过滤NaN(浮点数)和Null(字符串),以便能够与Php API进行通信。

from math import isnan

def clean_nones(value):
    """
    Recursively remove all None values from dictionaries and lists, and returns
    the result as a new dictionary or list.
    """
    def checkNan(value):
        if isinstance(value, float) and isnan(value):
            return True if (isinstance(value, float) and isnan(value)) else False
        
    if isinstance(value, list):
        return [clean_nones(x) for x in value if (x is not None and  x != 'NULL' and not checkNan(x))]
    elif isinstance(value, dict):
        return {
            key: clean_nones(val)
            for key, val in value.items()
            if (val is not None and val != 'NULL' and not checkNan(val))
        }
    else:
        return value

cleanedJson = clean_nones(toCleanJson)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接