When I encode and decode a custom iterable class with jsonpickle, the contained items are doubled.
I tried to use demjson and simplejson and I tried to implement this https://docs.python.org/2.5/ref/sequence-types.html. If I inherit from list it does work. But I don't want to inherit. It also works if I do not implement iter
I have a class like this:
import jsonpickle
from typing import *
class Product:
def __init__(self, name):
self.name = name
class Products:
def __init__(self):
self.__products: List[Product] = list()
def append(self, product: Product):
self.__products.append(product)
def __iter__(self):
return iter(self.__products)
def __next__(self):
return next(self.__products)
def __len__(self):
return len(self.__products)
def __getitem__(self, i):
return self.__products[i]
def extend(self, products: Iterable[Product]):
self.__products.extend(products)
When I use jsonpickle to encode this class and decode it again the contained products are doubled. The ValueError is raised in this example
if __name__ == '__main__':
products = Products()
products.append(Product('abc'))
encoded = jsonpickle.encode(products)
decoded_products = jsonpickle.decode(encoded)
if len(decoded_products) == 2:
raise ValueError()
If I use encoded = jsonpickle.encode(products, make_refs=False)
the second object is a string and not product
Do I have to implement any other method so that it works correctly?
I think jsonpickle is confused by the object, that looks like a sequence.
When decoding it, it first sets the value of __products as a complete list and later calls append for each element again.
I am not entirely sure why this happens, but you can visualize it using the following code inside Products:
def __setattr__(self, name, value):
super().__setattr__(name, value)
print("set", name, value)
def append(self, product: Product):
print("append", product)
self.__products.append(product)
You can fix it by implementing a custom pickle protocol like so:
class Products:
def __getstate__(self):
return self.__products
def __setstate__(self, state):
self.__products = state