以下代码遇到一个小问题,并带有递归错误:
100s错误打印结束于此:
RuntimeError: maximum recursion depth exceeded while calling a Python object
如下所示,我的代码不是递归的,因此DecimalEncoder会发生一些事情。
代码
import json
import decimal # tell json to leave my float values alone
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
return decimal.Decimal(o)
#return str(o)
return super(DecimalEncoder, self).default(o)
class JSONUtils:
def __init__( self, response ):
self.response = response
self.jsonData = None
self.LoadData( )
print 'jsonData: ' + json.dumps( self.jsonData, cls=DecimalEncoder, indent=2 )
def LoadData ( self ):
if ( self.jsonData == None ):
if ( type( self.response ) == str or type( self.response ) == unicode ):
#self.jsonData = json.loads(self.response )
self.jsonData = json.loads(self.response, parse_float=decimal.Decimal )
def GetJSONChunk( self, path ):
returnValue = ''
curPath = ''
try:
if ( type( path ) == str ):
returnValue = self.jsonData[path]
elif (type( path ) == list):
temp = ''
firstTime = True
for curPath in path:
if firstTime == True:
temp = self.jsonData[curPath]
firstTime = False
else:
temp = temp[curPath]
returnValue = temp
else:
print 'Unknown type in GetJSONChunk: ' + unicode( type( path ))
except KeyError as err:
ti.DBG_OUT( 'JSON chunk doesn\'t have value: ' + unicode( path ))
returnValue = self.kNoNode
except IndexError as err:
ti.DBG_OUT( 'Index does not exist: ' + unicode( curPath ))
returnValue = self.kInvalidIndex
return returnValue
myJSON = JSONUtils( unicode('{ "fldName":4.9497474683058327445566778899001122334455667788990011 }' ))
value = str( myJSON.GetJSONChunk ( 'fldName' ))
print str( type( value ))
print value
如果我为字符串交换返回decimal.Decimal(0)。它消除了错误但是你可以看到的值,以字符串形式返回。
#return decimal.Decimal(o)
return str(o)
这个输出很接近,但我需要一个类型的双倍:
jsonData: {
"fldName": "4.9497474683058327445566778899001122334455667788990011"
}
<type 'str'>
4.9497474683058327445566778899001122334455667788990011
如果我交换这些线条,您可以看到原始问题,这会导致精确度下降。
#self.jsonData = json.loads(self.response )
self.jsonData = json.loads(self.response, parse_float=decimal.Decimal )
答案 0 :(得分:1)
您遇到此递归错误,因为default
中的DecimalEncoder
方法返回的是它应该处理的相同类型,而不是将其转换为可序列化类型。
如果您在调试器中调试代码,您将能够在给定时刻看到JSONEncoder
的内部调用正在尝试将值转换为默认的可序列化类型之一,并且没有人匹配,它会调用后备,这是default
类中的DecimalEncoder
方法。
这就是声明return str(o)
有效的原因。它返回的是与Decimal
不同的类型,这是一个可序列化的类型。
我建议您按照user2357112的建议,考虑将Decimal
编码为字符串,并将其转换回另一端。
https://stackoverflow.com/a/38357877/7254201的答案指出了在JSON中使用字符串而不是Decimal
的一些原因。
下面我的建议是关于如何将小数转换为字符串,以便稍后可以获得原始值。
使用DecimalEncoder
将新的dict
中的小数值设为有一个名为value
的密钥,另一个名为type
,因此您将两者都设为字符串。当然,在反序列化值时会有一些进一步的工作。
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
return {
'value': str(o),
'type': str(type(o).__name__) # you can use another code to provide the type.
}
#return str(o)
return super(DecimalEncoder, self).default(o)
遵循此策略,您的代码输出如下所示:
jsonData: {
"fldName": {
"type": "decimal",
"value": "4.9497474683058327445566778899001122334455667788990011"
}
}
正如我所看到的,这个问题就是在序列化时保持值及其类型。在这些情况下,我尝试在可能的情况下将数据及其“元”信息分开。
如何解码
现在,您的对象如下:
"fldName": {
"type": "decimal",
"value": "4.9497474683058327445566778899001122334455667788990011"
}
您可能希望将其反序列化为:
{
"fldName": Decimal('4.9497474683058327445566778899001122334455667788990011')
}
因此,您可以根据此要点代码执行某些操作:https://gist.github.com/simonw/7000493
我将gist代码稍微更改为以下内容:
class DecimalDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, obj):
if 'type' not in obj:
return obj
obj_type = obj['type']
if obj_type == 'decimal':
return decimal.Decimal(obj.get('value'))
return obj
我的测试代码是:
jsonData = json.dumps( self.jsonData, cls=DecimalEncoder, indent=2 )
print 'jsonData: ' + jsonData
newObj = json.loads(jsonData, cls=DecimalDecoder)
print 'newObj: ' + str(newObj)
输出结果为:
jsonData: {
"fldName": {
"type": "decimal",
"value": "4.9497474683058327445566778899001122334455667788990011"
}
}
newObj: {u'fldName': Decimal('4.9497474683058327445566778899001122334455667788990011')}