JSON编码器的默认行为是将NaN转换为' NaN' json.dumps(np.NaN)导致NaN'。我怎样才能改变这个' NaN'值为' null'?
我试图将JSONEncoder子类化并实现default()方法,如下所示:
from json import JSONEncoder, dumps
import numpy as np
class NanConverter(JSONEncoder):
def default(self, obj):
try:
_ = iter(obj)
except TypeError:
if isinstance(obj, float) and np.isnan(obj):
return "null"
return JSONEncoder.default(self, obj)
>>> d = {'a': 1, 'b': 2, 'c': 3, 'e': np.nan, 'f': [1, np.nan, 3]}
>>> dumps(d, cls=NanConverter)
'{"a": 1, "c": 3, "b": 2, "e": NaN, "f": [1, NaN, 3]}'
预期结果:' {" a":1," c":3," b":2," e&# 34;:null," f":[1,null,3]}'
答案 0 :(得分:18)
这似乎达到了我的目标:
import simplejson
>>> simplejson.dumps(d, ignore_nan=True)
Out[3]: '{"a": 1, "c": 3, "b": 2, "e": null, "f": [1, null, 3]}'
答案 1 :(得分:5)
不幸的是,您可能需要使用@ Bramar的建议。你无法直接使用它。 Python的JSON编码器的The documentation声明:
如果指定,则default是为无法以其他方式序列化的对象调用的函数
您的NanConverter.default
方法甚至没有被调用,因为Python的JSON编码器已经知道如何序列化np.nan
。添加一些打印语句 - 您甚至不会调用您的方法。
答案 2 :(得分:4)
正如@Gerrat所指出的那样,你的钩dumps(d, cls=NanConverter)
很遗憾无法工作。
@ Alexander simplejson.dumps(d, ignore_nan=True)
有效但引入了额外的依赖关系(simplejson
)。
如果我们引入另一个依赖(pandas):
另一个明显的解决方案是dumps(pd.DataFrame(d).fillna(None))
,但Pandas issue 1972指出d.fillna(None)
会有不可预测的行为:
请注意,
fillna(None)
等同于fillna()
,这意味着value参数未使用。相反,它使用默认为前向填充的方法参数。
因此,请使用DataFrame.where
:
df = pd.DataFrame(d)
dumps(df.where(pd.notnull(df), None)))
答案 3 :(得分:1)
simplejson将在这里做正确的工作,但还有一个额外的标志包括:
尝试使用simplejson:
pip install simplejson
然后在代码中:
import simplejson
response = df.to_dict('records')
simplejson.dumps(response, ignore_nan=True,default=datetime.datetime.isoformat)
ignore_nan标志将正确处理所有NaN - >空转换
默认标志允许simplejson正确解析您的日期时间。
答案 4 :(得分:1)
答案 5 :(得分:1)
您可以使用 simplejson ,但是如果您只想使用JSON模块,那么我的把戏
json.dumps(d).replace(", NaN," , ', "null",')
答案 6 :(得分:0)
对于那些使用熊猫的人来说,最简单的方法-不需要第三方库:df.to_json。甚至可以在嵌套结构中转换NaN和其他Numpy类型:
df = pd.DataFrame({
'words': ['on', 'off'],
'lists': [
[[1, 1, 1], [2, 2, 2], [3, 3, 3]],
[[np.nan], [np.nan], [np.nan]],
'dicts': [
{'S': {'val': 'A'}},
{'S': {'val': np.nan}},
]
})
如果将其转换为字典列表,Pandas将保留本机nan
值:
json.dumps(df.to_dict(orient='record'))
> [{
"words": "on",
"lists": [[1, 1, 1], [2, 2, 2], [3, 3, 3]],
"dicts": {"S": {"val": "A"}}
},
{
"words": "off",
"lists": [[NaN], [NaN], [NaN]],
"dicts": {"S": {"val": NaN}}
}]
但是,如果您让Pandas将其直接转换为JSON字符串,它将为您解决:
df.to_json(orient='records')
> [{
"words": "on",
"lists": [[1,1,1],[2,2,2],[3,3,3]],
"dicts": {"S":{"val":"A"}}
},
{
"words": "off",
"lists": [[null],[null],[null]],
"dicts": {"S":{"val":null}}
}]
请注意,orient
和to_dict()
的{{1}}值略有不同。
答案 7 :(得分:0)
有一个PR可以在Python json标准库中自定义,但尚未合并。
答案 8 :(得分:0)
我使用以下解决方法:
json_constant_map = {
'-Infinity': float('-Infinity'),
'Infinity': float('Infinity'),
'NaN': None,
}
def json_nan_to_none(obj: typing.Any, *, default: typing.Callable = None) -> None:
# We want to convert NaNs to None and we have to use for now this workaround.
# We still want an exception for infinity and -infinity.
# See: https://github.com/python/cpython/pull/13233
json_string = json.dumps(obj, default=default)
return json.loads(
json_string,
parse_constant=lambda constant: json_constant_map[constant],
)