Python NaN JSON编码器

时间:2015-02-20 23:24:28

标签: python json numpy nan

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]}'

9 个答案:

答案 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)

  1. 正如@Gerrat所指出的那样,你的钩dumps(d, cls=NanConverter)很遗憾无法工作。

  2. @ Alexander simplejson.dumps(d, ignore_nan=True)有效但引入了额外的依赖关系(simplejson)。

  3. 如果我们引入另一个依赖(pandas):

    1. 另一个明显的解决方案是dumps(pd.DataFrame(d).fillna(None)),但Pandas issue 1972指出d.fillna(None)会有不可预测的行为:

        

      请注意,fillna(None)等同于fillna(),这意味着value参数未使用。相反,它使用默认为前向填充的方法参数。

    2. 因此,请使用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)

如果您使用的是pandas

df = df.replace({pd.np.nan: None})

Github issue上给这个人信贷。

答案 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}}
  }]

请注意,orientto_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],
    )