我在json中编码无穷大时遇到了麻烦。
json.dumps
会将此转换为"Infinity"
,但我希望将其转换为null
或我选择的其他值。
不幸的是,如果转储尚未理解该对象,则设置default
参数似乎才有效,否则默认处理程序似乎被绕过。
有没有办法可以预编码对象,更改类型/类编码的默认方式,或者在正常编码之前将某个类型/类转换为不同的对象?
答案 0 :(得分:6)
在这里查看来源:http://hg.python.org/cpython/file/7ec9255d4189/Lib/json/encoder.py
如果你是JSONEncoder的子类,你可以只覆盖iterencode(self, o, _one_shot=False)
方法,该方法具有明确的Infinity特殊外壳(在内部函数内)。
为了使其可重复使用,您还需要更改__init__
以获取一些新选项,并将它们存储在类中。
或者,您可以从pypi中选择一个具有您正在寻找的适当扩展性的json库:https://pypi.python.org/pypi?%3Aaction=search&term=json&submit=search
以下是一个例子:
import json
class FloatEncoder(json.JSONEncoder):
def __init__(self, nan_str = "null", **kwargs):
super(FloatEncoder,self).__init__(**kwargs)
self.nan_str = nan_str
#uses code from official python json.encoder module. Same licence applies.
def iterencode(self, o, _one_shot=False):
"""Encode the given object and yield each string
representation as available.
For example::
for chunk in JSONEncoder().iterencode(bigobject):
mysocket.write(chunk)
"""
if self.check_circular:
markers = {}
else:
markers = None
if self.ensure_ascii:
_encoder = json.encoder.encode_basestring_ascii
else:
_encoder = json.encoder.encode_basestring
if self.encoding != 'utf-8':
def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
if isinstance(o, str):
o = o.decode(_encoding)
return _orig_encoder(o)
def floatstr(o, allow_nan=self.allow_nan,
_repr=json.encoder.FLOAT_REPR, _inf=json.encoder.INFINITY, _neginf=-json.encoder.INFINITY, nan_str = self.nan_str):
# Check for specials. Note that this type of test is processor
# and/or platform-specific, so do tests which don't depend on the
# internals.
if o != o:
text = nan_str
elif o == _inf:
text = 'Infinity'
elif o == _neginf:
text = '-Infinity'
else:
return _repr(o)
if not allow_nan:
raise ValueError(
"Out of range float values are not JSON compliant: " +
repr(o))
return text
_iterencode = json.encoder._make_iterencode(
markers, self.default, _encoder, self.indent, floatstr,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, _one_shot)
return _iterencode(o, 0)
example_obj = {'name': 'example', 'body': [1.1, {"3.3": 5, "1.1": float('Nan')}, [float('inf'), 2.2]]}
print json.dumps(example_obj, cls=FloatEncoder)
ideone:http://ideone.com/dFWaNj
答案 1 :(得分:5)
不,没有简单的方法可以实现这一目标。事实上,根据标准,NaN
和Infinity
浮点值根本不应该用json序列化。
Python使用标准的扩展。您可以使符合标准的python编码将allow_nan=False
参数传递给dumps
,但即使您提供{{1},这也会为infinity / nans 引发ValueError
功能。
你有两种方法可以做你想做的事:
子类default
并更改这些值的编码方式。请注意,您必须考虑序列可以包含无穷大值等的情况.AFAIK没有API来重新定义特定类的对象如何编码。
制作要编码的对象的副本,并用JSONEncoder
或其他根据需要编码的其他对象替换任何出现的infinity / nan。
不太健壮,但更简单的解决方案是修改编码数据,例如用None
替换所有Infinity
子串:
null
显然你应该考虑字符串中的文本>>> import re
>>> infty_regex = re.compile(r'\bInfinity\b')
>>> def replace_infinities(encoded):
... regex = re.compile(r'\bInfinity\b')
... return regex.sub('null', encoded)
...
>>> import json
>>> replace_infinities(json.dumps([1, 2, 3, float('inf'), 4]))
'[1, 2, 3, null, 4]'
等,所以即使在这里,强大的解决方案也不是直接的,也不是优雅的。
答案 2 :(得分:0)
你可以沿着这些方向做点什么:
import json
import math
target=[1.1,1,2.2,float('inf'),float('nan'),'a string',int(2)]
def ffloat(f):
if not isinstance(f,float):
return f
if math.isnan(f):
return 'custom NaN'
if math.isinf(f):
return 'custom inf'
return f
print 'regular json:',json.dumps(target)
print 'customized:',json.dumps(map(ffloat,target))
打印:
regular json: [1.1, 1, 2.2, Infinity, NaN, "a string", 2]
customized: [1.1, 1, 2.2, "custom inf", "custom NaN", "a string", 2]
如果你想处理嵌套的数据结构,这也不是那么难:
import json
import math
from collections import Mapping, Sequence
def nested_json(o):
if isinstance(o, float):
if math.isnan(o):
return 'custom NaN'
if math.isinf(o):
return 'custom inf'
return o
elif isinstance(o, basestring):
return o
elif isinstance(o, Sequence):
return [nested_json(item) for item in o]
elif isinstance(o, Mapping):
return dict((key, nested_json(value)) for key, value in o.iteritems())
else:
return o
nested_tgt=[1.1,{1.1:float('inf'),3.3:5},(float('inf'),2.2),]
print 'regular json:',json.dumps(nested_tgt)
print 'nested json',json.dumps(nested_json(nested_tgt))
打印:
regular json: [1.1, {"3.3": 5, "1.1": Infinity}, [Infinity, 2.2]]
nested json [1.1, {"3.3": 5, "1.1": "custom inf"}, ["custom inf", 2.2]]
答案 3 :(得分:0)
我遇到了这个问题并且不想为了处理这种情况而为项目带来额外的依赖。此外,我的项目支持Python 2.6,2.7,3.3和3.4以及simplejson
的用户。不幸的是,在这些版本之间存在三种不同的iterencode
实现,因此不希望对特定版本进行硬编码。
希望这可以帮助其他有类似要求的人!
如果与项目的其他组件相比,json.dumps
调用周围的编码时间/处理能力较小,则可以取消编码/重新编码JSON,以获得所需的结果{{1 kwarg。
parse_constant
,Python 3.x&#39 {s} json
或使用json
,则无关紧要(例如,simplejson
)import simplejson as json
接口。json