简体   繁体   English

使用jsonpickle进行类型演变(python)

[英]type evolution with jsonpickle (python)

Is there any support of this in jsonpickle? jsonpickle中对此有任何支持吗?

Eg I store and object, them modify its schema, then try to load it back. 例如,我存储并反对,他们修改其架构,然后尝试将其加载回去。

The following change, for instance, (attribute addition) 例如,以下更改(属性添加)

import jsonpickle

class Stam(object):

   def __init__(self, a):
     self.a = a

   def __str__(self):
     return '%s with a=%s' % (self.__class__.__name__, str(self.a))


js = jsonpickle.encode(Stam(123))
print 'encoded:', js

class Stam(object):

   def __init__(self, a, b):
     self.a = a
     self.b = b

   def __str__(self):
     return '%s with a=%s, b=%s' % (self.__class__.__name__, str(self.a), str(self.b))

s=jsonpickle.decode(js)
print 'decoded:', s

produces an error: 产生一个错误:

encoded: {"py/object": "__main__.Stam", "a": 123}
decoded: Traceback (most recent call last):
  File "C:\gae\google\appengine\ext\admin\__init__.py", line 317, in post
    exec(compiled_code, globals())
  File "<string>", line 25, in <module>
  File "<string>", line 22, in __str__
AttributeError: 'Stam' object has no attribute 'b'

There is no support for type evolution or type migrations within jsonpickle. jsonpickle中不支持类型演变或类型迁移。

Your best course of action would be to load (via json.loads ) the JSON representation of your data into a basic Python structure of lists / dicts / strings / numbers. 最好的做法是将数据的JSON表示(通过json.loads )加载到列表/字典/字符串/数字的基本Python结构中。 Traverse this Python representation, adding in empty/default b keys. 遍历此Python表示形式,添加空/默认b键。 Then re-save the JSON via json.dumps . 然后通过json.dumps重新保存JSON。

You can then use jsonpickle to load the modified version of the data. 然后,您可以使用jsonpickle加载数据的修改版本。

temp = json.loads(js)
temp['b'] = None
js = json.dumps(temp)
jsonpickle.decode(js)

This obviously gets more complicated if your object model is more complex, but you can check the py/object key to see if you need to modify the object. 如果对象模型更复杂,这显然会变得更加复杂,但是您可以检查py / object键以查看是否需要修改对象。

Because of the versioning problem, jsonpickle alone is not a sufficient for persisting objects. 由于版本问题,仅jsonpickle不足以持久化对象。 You also need to keep a version identifier in the JSON output so that you can retrofit (cleanup) the data when you are reading an older version. 您还需要在JSON输出中保留版本标识符,以便在读取旧版本时可以对数据进行改造(清理)。

With that said, there are somethings you can do to make life easier. 话虽如此,您可以做一些事情来使生活更轻松。 You can use the default=dict parameter of json.dumps in conjunction with iter on your object. 您可以将json.dumps的default = dict参数与iter结合使用。 This will let you persist your object as a dictionary. 这将使您可以将对象持久保存为字典。 Then when you read it in you can use the **dict operator and key word arguments to re-instantiate your object from the JSON dictionary. 然后,当您阅读它时,可以使用** dict运算符和关键字参数从JSON字典重新实例化对象。

This allows you to read in your persisted objects and supply initialization for any new attributes. 这使您可以读取持久对象并为任何新属性提供初始化。 For example if we start with an class that has a val1 attribute and persist it, then expand the class to have a val2 attribute and restore if from the persisted state: 例如,如果我们从具有val1属性的类开始并对其进行持久化,然后将其扩展为具有val2属性,并从持久状态恢复:

import json

class Stam( object ) :
    val1 = None
    def __init__( self, val1=None ) :
        self.val1 = val1

    def __iter__( self ) : return {
        'val1':self.val1
    }.iteritems()

obj1 = Stam( val1='a' )
persisted = json.dumps( obj1, default=dict )

class Stam( object ) :
    val1 = None
    val2 = None
    def __init__( self, val1=None, val2='b' ) :
        self.val1 = val1
        self.val2 = val2

    def __iter__( self ) : return {
        'val1':self.val1,
        'val2':self.val2
    }.iteritems()

obj2 = json.loads( persisted, object_hook=lambda d: Stam(**d) )
assert obj2.val1 == 'a'
assert obj2.val2 == 'b'

Of course, we could also use jsonpickle and skip the __iter__ and extra json arguments because jsonpickle will ignore the missing attributes. 当然,我们也可以使用jsonpickle并跳过__iter__和其他json参数,因为jsonpickle将忽略缺少的属性。 Thus any new val2 would have the static class initialization supplied, but it would not run the initialization code in the __init__ ctor. 因此,任何新的val2都将提供静态类初始化,但不会在__init__ ctor中运行初始化代码。 This would become: 这将变成:

import jsonpickle

class Stam( object ) :
    val1 = None
    def __init__( self, val1 ) :
        self.val1 = val1

obj1 = Stam( 'a' )
persisted = jsonpickle.encode( obj1 )

class Stam( object ) :
    val1 = None
    val2 = 'b'
    def __init__( self, val1, val2 ) :
        self.val1 = val1
        self.val2 = val2

obj2 = jsonpickle.decode( persisted )
assert obj2.val1 == 'a'
assert obj2.val2 == 'b'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM