Last active
January 21, 2023 01:33
-
-
Save acdha/e5392a4d41396eeeab0c7202e683a6e4 to your computer and use it in GitHub Desktop.
Example of how to exclude Nones from JSON-encoded output, optionally destructively
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Example of how to non-destructively remove None values from an object before | |
JSON serialization | |
""" | |
import json | |
def filter_nones(obj): | |
""" | |
Given a JSON-serializable object, return a copy without elements which have | |
a value of None. | |
""" | |
if isinstance(obj, list): | |
# This version may or may not be easier to read depending on your | |
# preferences: | |
# return list(filter(None, map(remove_nones, obj))) | |
# This version uses a generator expression to avoid computing a full | |
# list only to immediately walk it again: | |
filtered_values = (filter_nones(j) for j in obj) | |
return [i for i in filtered_values if i is not None] | |
elif isinstance(obj, dict): | |
filtered_items = ((i, filter_nones(j)) for i, j in obj.items()) | |
return {k: v for k, v in filtered_items if v is not None} | |
else: | |
return obj | |
def remove_nones(obj): | |
""" | |
Given a JSON-serializable object, modify it in place to remove elements | |
which have a value of None | |
""" | |
if isinstance(obj, list): | |
# We can't use list.remove() while iterating because that will cause the | |
# next element in the list to be skipped. | |
# Slice assignment alters the list in-place in case there are any other | |
# references to it: | |
# for i in obj: | |
# remove_nones(i) | |
# obj[:] = [i for i in obj if i is not None] | |
# … or we can walk the list in reverse to avoid a second pass: | |
for i in range(len(obj) - 1, -1, -1): | |
v = obj[i] | |
if v is None: | |
obj.pop(i) | |
else: | |
remove_nones(v) | |
elif isinstance(obj, dict): | |
# We're forced to create a list first to avoid an exception caused by | |
# changing the dictionary size while iterating: | |
for k, v in list(obj.items()): | |
if v is None: | |
obj.pop(k) | |
else: | |
remove_nones(v) | |
resp = { | |
"scalar": "test", | |
"list": ["foo", None, "bar"], | |
"nested_dict": { | |
"a": 1, | |
"b": ["test", None, "test2"], | |
"d": None, | |
"e": [ | |
None, | |
{ | |
"k": None, | |
"a": "b", | |
}, | |
], | |
}, | |
"scalar2": None, | |
"nested_list": [ | |
"foo", | |
[None], | |
["bar", "baaz"], | |
], | |
} | |
print(json.dumps(resp, indent=4)) | |
print(json.dumps(filter_nones(resp), indent=4)) | |
remove_nones(resp) | |
print(json.dumps(resp, indent=4)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment