Python NaN JSON编码器

Ale*_*der 23 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]}'
Run Code Online (Sandbox Code Playgroud)

预期结果:'{"a":1,"c":3,"b":2,"e":null,"f":[1,null,3]}'

Ale*_*der 18

这似乎达到了我的目标:

import simplejson


>>> simplejson.dumps(d, ignore_nan=True)
Out[3]: '{"a": 1, "c": 3, "b": 2, "e": null, "f": [1, null, 3]}'
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法在内置的json包中获得此行为?我宁愿不再依赖第三方包. (6认同)
  • 我找不到。 (3认同)

Han*_*ter 15

你可以重写encodePythonJSONEncoder类的方法,预处理obj并替换nanNone(一旦序列化就会变成null)。

这似乎是最直接的方法,因为正如@Gerrat指出的那样,Python JSONEncoderdefault在遇到nan. 即使在调用dump/dumps时,allow_nan=False它也会在给用户“做自己的事情”的机会之前抛出异常。

import math
from json import JSONEncoder, dumps

def nan2None(obj):
    if isinstance(obj, dict):
        return {k:nan2None(v) for k,v in obj.items()}
    elif isinstance(obj, list):
        return [nan2None(v) for v in obj]
    elif isinstance(obj, float) and math.isnan(obj):
        return None
    return obj

class NanConverter(JSONEncoder):
    def encode(self, obj, *args, **kwargs):
        return super().encode(nan2None(obj), *args, **kwargs)

>>> import numpy as np  # only needed as example also contains np.nan
>>> d = {'a': 1, 'b': 2, 'c': 3, 'e': math.nan, 'f': [1, np.nan, 3]}
>>> dumps(d, cls=NanConverter)
'{"a": 1, "b": 2, "c": 3, "e": null, "f": [1, null, 3]}'
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案!_注意:由于“JSONEncoder.iterencode()”是从“JSONEncoder.encode()”调用的,因此您实际上不必调用“nan2None()”两次 - 转换“NanConverter.encode()”中的数据应该足够了)`._ (2认同)

eiT*_*aVi 7

simplejson 会在这里做正确的工作,但是还有一个额外的标志值得包含:

尝试使用 simplejson:

pip install simplejson
Run Code Online (Sandbox Code Playgroud)

然后在代码中:

import simplejson

response = df.to_dict('records')
simplejson.dumps(response, ignore_nan=True,default=datetime.datetime.isoformat)
Run Code Online (Sandbox Code Playgroud)

ignore_nan 标志将正确处理所有 NaN --> null 转换

默认标志将允许 simplejson 正确解析您的日期时间

  • 我可以建议您添加有关“default”参数的解释作为对已接受答案的评论吗?似乎比用与OP的问题无关的另一个参数复制已接受的答案更有意义。 (4认同)

rod*_*ira 7

使用熊猫

对于那些使用 Pandas 的人来说,最简单的方法 - 不需要第三方库: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}},
  ]
})
Run Code Online (Sandbox Code Playgroud)

如果将其转换为字典列表,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}}
  }]
Run Code Online (Sandbox Code Playgroud)

但如果你让 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}}
  }]
Run Code Online (Sandbox Code Playgroud)

请注意,和orient之间的值略有不同。to_dict()to_json()

使用标准库

如果您只是使用列表、字典和标量值,则可以手动转换 NaN:

import math

def to_none(val):
    if math.isnan(val):
        return None
    return val
Run Code Online (Sandbox Code Playgroud)


Ger*_*rat 5

不幸的是,你可能需要使用@ Bramar的建议.你无法直接使用它. Python的JSON编码器的文档说明:

如果指定,则default是为无法以其他方式序列化的对象调用的函数

您的NanConverter.default方法甚至没有被调用,因为Python的JSON编码器已经知道如何序列化np.nan.添加一些打印语句 - 您甚至不会调用您的方法.


Mic*_*rie 5

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

  2. @Alexander的simplejson.dumps(d, ignore_nan=True)作品,但引入了附加的依赖项(simplejson)。

如果我们引入另一个依赖关系(熊猫):

  1. 另一个明显的解决方法是dumps(pd.DataFrame(d).fillna(None)),但是1972年熊猫出版(Pandas issue 1972)指出,这种d.fillna(None)行为将具有不可预测的行为:

    请注意,fillna(None)它等效于fillna(),这意味着未使用value参数。而是使用默认为正向填充的method参数。

  2. 因此,改用DataFrame.where

    df = pd.DataFrame(d)
    dumps(df.where(pd.notnull(df), None)))
    
    Run Code Online (Sandbox Code Playgroud)

  • 此代码不适用于 OP 的“d”。即使您修复此代码片段以使用“d.items()”而不是“d”,并从“json”导入“dumps”,列表中的“nan”也不会转换。 (2认同)