这个问题与 jsonpickle (python) 的类型演化有关
当前状态说明:
我需要在 python 中使用 jsonpickle 将对象存储到 JSON 文件中。
对象类CarState
是由另一个软件组件的脚本生成的,因此我无法更改类本身。此脚本自动为 jsonpickle 用于序列化对象的类生成__getstate__
和方法。仅返回每个成员变量的值列表,没有字段名称__setstate__
。__getstate__
因此 jsonpickle 不存储字段名称,而只存储 JSON 数据中的值(参见下面的代码示例)
问题:
假设我的程序需要CarState
通过附加字段 ( CarStateNewVersion
) 为新版本(版本 2)扩展类。现在如果它从版本 1 加载 JSON 数据,则数据不会分配给正确的字段。
这是一个演示问题的示例代码。该类CarState
由脚本生成并在此处简化以显示问题。在版本 2 中,我CarState
用一个新字段更新了类(在插入的代码中,CarStateNewVersion
为了保持简单)
#!/usr/bin/env python
import jsonpickle as jp
# Class using slots and implementing the __getstate__ method
# Let's say this is in program version 1
class CarState(object):
__slots__ = ['company','type']
_slot_types = ['string','string']
def __init__(self):
self.company = ""
self.type = ""
def __getstate__(self):
return [getattr(self, x) for x in self.__slots__]
def __setstate__(self, state):
for x, val in zip(self.__slots__, state):
setattr(self, x, val)
# Class using slots and implementing the __getstate__ method
# For program version 2 a new field 'year' is needed
class CarStateNewVersion(object):
__slots__ = ['company','year','type']
_slot_types = ['string','string','string']
def __init__(self):
self.company = ""
self.type = ""
self.year = "1900"
def __getstate__(self):
return [getattr(self, x) for x in self.__slots__]
def __setstate__(self, state):
for x, val in zip(self.__slots__, state):
setattr(self, x, val)
# Class using slots without the __getstate__ method
# Let's say this is in program version 1
class CarDict(object):
__slots__ = ['company','type']
_slot_types = ['string','string']
def __init__(self):
self.company = ""
self.type = ""
# Class using slots without the __getstate__ method
# For program version 2 a new field 'year' is needed
class CarDictNewVersion(object):
__slots__ = ['company','year','type']
_slot_types = ['string','string','string']
def __init__(self):
self.company = ""
self.type = ""
self.year = "1900"
if __name__ == "__main__":
# Version 1 stores the data
carDict = CarDict()
carDict.company = "Ford"
carDict.type = "Mustang"
print jp.encode(carDict)
# {"py/object": "__main__.CarDict", "company": "Ford", "type": "Mustang"}
# Now version 2 tries to load the data
carDictNewVersion = jp.decode('{"py/object": "__main__.CarDictNewVersion", "company": "Ford", "type": "Mustang"}')
# OK!
# carDictNewVersion.company = Ford
# carDictNewVersion.year = undefined
# carDictNewVersion.type = Mustang
# Version 1 stores the data
carState = CarState()
carState.company = "Ford"
carState.type = "Mustang"
print jp.encode(carState)
# {"py/object": "__main__.CarState", "py/state": ["Ford", "Mustang"]}
# Now version 2 tries to load the data
carStateNewVersion = jp.decode('{"py/object": "__main__.CarStateNewVersion", "py/state": ["Ford", "Mustang"]}')
# !!!! ERROR !!!!
# carDictNewVersion.company = Ford
# carDictNewVersion.year = Mustang
# carDictNewVersion.type = undefined
try:
carDictNewVersion.year
except:
carDictNewVersion.year = 1900
正如你所看到的CarDict
andCarDictNewVersion
类,如果__getstate__
没有实现,那么新添加的字段没有问题,因为JSON文本也包含字段名称。
问题:
是否有可能告诉 jsonpickle 不使用__getstate__
并使用它__dict__
来包含 JSON 数据中的字段名称?还是有另一种可能性以某种方式包含字段名称?
注意:我无法更改CarState
类或包含__getstate__
方法,因为它是通过另一个软件组件的脚本生成的。我只能在 main 方法中更改代码。
或者是否有另一种用于创建人类可读输出并包含字段名称的 python 序列化工具?
附加背景信息:
该类是使用 ROS 中的消息定义生成的,即由genpy
生成的,生成的类继承自Message
实现的类__getstate__
(参见https://github.com/ros/genpy/blob/indigo-devel/src /genpy/message.py#L308 )