带有 simplejson 后端的 jsonpickle 将 Decimal 序列化为 null
jsonpickle with simplejson backend serializes Decimal as null
我正在尝试使用 python 3.7 中的 jsonpickle 将对象树序列化为 json。但是,所有 Decimal
都被序列化为 null
。我使用 simplejson 作为后端,所以它应该能够序列化 Decimals。
如何将(复杂的)对象树序列化为 json,包括小数?
示例代码(需要安装 simplejson 和 jsonpickle):
预期的序列化 json 应该是 {"amount": 1.0}
,我不想使用 float
,因为舍入错误。
import jsonpickle
from decimal import Decimal
jsonpickle.set_preferred_backend('simplejson')
jsonpickle.set_encoder_options('simplejson', use_decimal=True)
class MyClass():
def __init__(self, amount):
self.amount = amount
def to_json(self):
return jsonpickle.dumps(self, unpicklable=False)
if __name__ == '__main__':
obj = MyClass(Decimal('1.0'))
print(obj.to_json()) # prints '{"amount": null}'
PS 我不关心使用 jsonpickle。因此,也欢迎使用 jsonpickle 的替代方法将复杂对象树序列化为 json(包括 Decimal 字段)。
您需要注册一个处理程序来处理十进制class
import jsonpickle
from decimal import Decimal
jsonpickle.set_preferred_backend('simplejson')
jsonpickle.set_encoder_options('simplejson', use_decimal=True)
class DecimalHandler(jsonpickle.handlers.BaseHandler):
def flatten(self, obj, data):
return obj.__str__() #Convert to json friendly format
jsonpickle.handlers.registry.register(Decimal, DecimalHandler)
class MyClass():
def __init__(self, amount):
self.amount = amount
def to_json(self):
return jsonpickle.dumps(self, unpicklable=False)
if __name__ == '__main__':
obj = MyClass(Decimal('1.0'))
print(obj.to_json())
更新的答案:jsonpickle 的主分支现在有一个 use_decimal 模式,允许您在没有任何自定义处理程序的情况下实现这个结果。
import decimal
import unittest
import jsonpickle
class Example(object):
"""Example class holding a Decimal"""
def __init__(self, amount):
self.amount = decimal.Decimal(amount)
class UseDecimalTestCase(unittest.TestCase):
"""Demonstrate the new use_decimal mode"""
def test_use_decimal(self):
obj = Example(0.5)
# Configure simplejson to use decimals.
jsonpickle.set_encoder_options('simplejson', use_decimal=True, sort_keys=True)
jsonpickle.set_preferred_backend('simplejson')
as_json = jsonpickle.dumps(obj, unpicklable=False, use_decimal=True)
print(as_json)
# {"amount": 0.5}
# Configure simplejson to get back Decimal when restoring from json.
jsonpickle.set_decoder_options('simplejson', use_decimal=True)
obj_clone = jsonpickle.loads(as_json)
# NOTE: we get back a dict, not an Example instance.
self.assertTrue(isinstance(obj_clone, dict))
# But, the Decimal *is* preserved
self.assertTrue(isinstance(obj_clone['amount'], decimal.Decimal))
self.assertEqual(obj.amount, obj_clone['amount'])
# Side-effect of simplejson decimal mode:
# floats become Decimal when round-tripping
obj.amount = 0.5 # float
as_json = jsonpickle.dumps(obj, unpicklable=False)
obj_clone = jsonpickle.loads(as_json)
self.assertTrue(isinstance(obj_clone['amount'], decimal.Decimal))
if __name__ == '__main__':
unittest.main()
相关问题:
https://github.com/jsonpickle/jsonpickle/issues/244
对于旧的 jsonpickle 版本:
这可以通过允许 simplejson 进行编码的自定义传递处理程序来完成。您必须同时配置编码器和解码器选项,以便返回小数。如果您不关心往返,那么用例会更简单。
import decimal
import unittest
import jsonpickle
from jsonpickle.handlers import BaseHandler
class SimpleDecimalHandler(BaseHandler):
"""Simple pass-through handler so that simplejson can do the encoding"""
def flatten(self, obj, data):
return obj
def restore(self, obj):
return obj
class Example(object):
"""Example class holding a Decimal"""
def __init__(self, amount):
self.amount = decimal.Decimal(amount)
class DecimalTestCase(unittest.TestCase):
"""Test Decimal json serialization"""
def test_custom_handler(self):
obj = Example(0.5)
# Enable the simplejson Decimal handler -- slightly simpler than jsonpickle's
# default handler which does the right thing already.
# If you don't care about the json representation then you don't
# need to do anything -- jsonpickle preserves decimal by default
# when using its default dumps() options.
#
# We use this decimal handler so that simplejson does the encoding
# rather than jsonpickle. Thus, we have to configure simplejson too,
# which is not needed otherwise when using jsonpickle's defaults.
jsonpickle.set_encoder_options('simplejson', use_decimal=True, sort_keys=True)
jsonpickle.set_decoder_options('simplejson', use_decimal=True)
jsonpickle.set_preferred_backend('simplejson')
SimpleDecimalHandler.handles(decimal.Decimal)
as_json = jsonpickle.dumps(obj)
print(as_json)
# {"amount": 0.5, "py/object": "__main__.Example"}
# NOTE: this comes back as an Example instance
clone = jsonpickle.loads(as_json)
self.assertTrue(isinstance(clone, Example))
self.assertTrue(isinstance(clone.amount, decimal.Decimal))
self.assertEqual(obj.amount, clone.amount)
# We can simplify the JSON representation a little further
# by using unpickleable=False, but we lose the Example class.
as_json = jsonpickle.dumps(obj, unpicklable=False)
# Upside: this prints {"amount": 0.5}
# Downside: this object cannot be reconstructed back into an
# instance of the Example class.
print(as_json)
# NOTE: we get back a dict, not an Example instance.
obj_clone = jsonpickle.loads(as_json)
self.assertTrue(isinstance(obj_clone, dict))
# But, the Decimal *is* preserved
self.assertTrue(isinstance(obj_clone['amount'], decimal.Decimal))
self.assertEqual(obj.amount, obj_clone['amount'])
if __name__ == '__main__':
unittest.main()
我正在尝试使用 python 3.7 中的 jsonpickle 将对象树序列化为 json。但是,所有 Decimal
都被序列化为 null
。我使用 simplejson 作为后端,所以它应该能够序列化 Decimals。
如何将(复杂的)对象树序列化为 json,包括小数?
示例代码(需要安装 simplejson 和 jsonpickle):
预期的序列化 json 应该是 {"amount": 1.0}
,我不想使用 float
,因为舍入错误。
import jsonpickle
from decimal import Decimal
jsonpickle.set_preferred_backend('simplejson')
jsonpickle.set_encoder_options('simplejson', use_decimal=True)
class MyClass():
def __init__(self, amount):
self.amount = amount
def to_json(self):
return jsonpickle.dumps(self, unpicklable=False)
if __name__ == '__main__':
obj = MyClass(Decimal('1.0'))
print(obj.to_json()) # prints '{"amount": null}'
PS 我不关心使用 jsonpickle。因此,也欢迎使用 jsonpickle 的替代方法将复杂对象树序列化为 json(包括 Decimal 字段)。
您需要注册一个处理程序来处理十进制class
import jsonpickle
from decimal import Decimal
jsonpickle.set_preferred_backend('simplejson')
jsonpickle.set_encoder_options('simplejson', use_decimal=True)
class DecimalHandler(jsonpickle.handlers.BaseHandler):
def flatten(self, obj, data):
return obj.__str__() #Convert to json friendly format
jsonpickle.handlers.registry.register(Decimal, DecimalHandler)
class MyClass():
def __init__(self, amount):
self.amount = amount
def to_json(self):
return jsonpickle.dumps(self, unpicklable=False)
if __name__ == '__main__':
obj = MyClass(Decimal('1.0'))
print(obj.to_json())
更新的答案:jsonpickle 的主分支现在有一个 use_decimal 模式,允许您在没有任何自定义处理程序的情况下实现这个结果。
import decimal
import unittest
import jsonpickle
class Example(object):
"""Example class holding a Decimal"""
def __init__(self, amount):
self.amount = decimal.Decimal(amount)
class UseDecimalTestCase(unittest.TestCase):
"""Demonstrate the new use_decimal mode"""
def test_use_decimal(self):
obj = Example(0.5)
# Configure simplejson to use decimals.
jsonpickle.set_encoder_options('simplejson', use_decimal=True, sort_keys=True)
jsonpickle.set_preferred_backend('simplejson')
as_json = jsonpickle.dumps(obj, unpicklable=False, use_decimal=True)
print(as_json)
# {"amount": 0.5}
# Configure simplejson to get back Decimal when restoring from json.
jsonpickle.set_decoder_options('simplejson', use_decimal=True)
obj_clone = jsonpickle.loads(as_json)
# NOTE: we get back a dict, not an Example instance.
self.assertTrue(isinstance(obj_clone, dict))
# But, the Decimal *is* preserved
self.assertTrue(isinstance(obj_clone['amount'], decimal.Decimal))
self.assertEqual(obj.amount, obj_clone['amount'])
# Side-effect of simplejson decimal mode:
# floats become Decimal when round-tripping
obj.amount = 0.5 # float
as_json = jsonpickle.dumps(obj, unpicklable=False)
obj_clone = jsonpickle.loads(as_json)
self.assertTrue(isinstance(obj_clone['amount'], decimal.Decimal))
if __name__ == '__main__':
unittest.main()
相关问题:
https://github.com/jsonpickle/jsonpickle/issues/244
对于旧的 jsonpickle 版本:
这可以通过允许 simplejson 进行编码的自定义传递处理程序来完成。您必须同时配置编码器和解码器选项,以便返回小数。如果您不关心往返,那么用例会更简单。
import decimal
import unittest
import jsonpickle
from jsonpickle.handlers import BaseHandler
class SimpleDecimalHandler(BaseHandler):
"""Simple pass-through handler so that simplejson can do the encoding"""
def flatten(self, obj, data):
return obj
def restore(self, obj):
return obj
class Example(object):
"""Example class holding a Decimal"""
def __init__(self, amount):
self.amount = decimal.Decimal(amount)
class DecimalTestCase(unittest.TestCase):
"""Test Decimal json serialization"""
def test_custom_handler(self):
obj = Example(0.5)
# Enable the simplejson Decimal handler -- slightly simpler than jsonpickle's
# default handler which does the right thing already.
# If you don't care about the json representation then you don't
# need to do anything -- jsonpickle preserves decimal by default
# when using its default dumps() options.
#
# We use this decimal handler so that simplejson does the encoding
# rather than jsonpickle. Thus, we have to configure simplejson too,
# which is not needed otherwise when using jsonpickle's defaults.
jsonpickle.set_encoder_options('simplejson', use_decimal=True, sort_keys=True)
jsonpickle.set_decoder_options('simplejson', use_decimal=True)
jsonpickle.set_preferred_backend('simplejson')
SimpleDecimalHandler.handles(decimal.Decimal)
as_json = jsonpickle.dumps(obj)
print(as_json)
# {"amount": 0.5, "py/object": "__main__.Example"}
# NOTE: this comes back as an Example instance
clone = jsonpickle.loads(as_json)
self.assertTrue(isinstance(clone, Example))
self.assertTrue(isinstance(clone.amount, decimal.Decimal))
self.assertEqual(obj.amount, clone.amount)
# We can simplify the JSON representation a little further
# by using unpickleable=False, but we lose the Example class.
as_json = jsonpickle.dumps(obj, unpicklable=False)
# Upside: this prints {"amount": 0.5}
# Downside: this object cannot be reconstructed back into an
# instance of the Example class.
print(as_json)
# NOTE: we get back a dict, not an Example instance.
obj_clone = jsonpickle.loads(as_json)
self.assertTrue(isinstance(obj_clone, dict))
# But, the Decimal *is* preserved
self.assertTrue(isinstance(obj_clone['amount'], decimal.Decimal))
self.assertEqual(obj.amount, obj_clone['amount'])
if __name__ == '__main__':
unittest.main()