Сериализация/Десериализация простого, но вложенного объекта в/из JSON

#python #object #json-serialization

Вопрос:

Я пытаюсь передать конфигурацию с клиента на сервер.

  • Конфигурация содержит password ключ, который я НЕ должен передавать
  • Конфигурация содержит пару простых объектов, которые являются просто парами ключ/значение (значение является базовым примитивом).

Этот код работает:

 class Empty:
    pass

class Config:
    def __init__(self):
        # don't want to transmit this over the internet
        self.secret = 'P@ssw0rd'

    def create(self, foo):
        self.foo = foo  # property passed in
        self.bar = f'Hello {foo}'  # calculated property

        # A couple of custom objects, but they are simple
        # (only containing key/value pairs where value is basic primitive)
        self.v = Empty()
        self.v.a = 1

        self.w = Empty()
        self.w.b = 2

    def export_json(self):
        J = {}
        for k, v in vars(self).items():
            if k == 'secret':
                continue
            J[k] = vars(v) if isinstance(v, Empty) else v
        return J

    def construct_from_json(self, J_str):
        J = json.loads(J_str)
        for k, v in J.items():
            if isinstance(v, dict):
                _ = Empty()
                for k_, v_ in v.items():
                    setattr(_, k_, v_)
                v = _
            setattr(self, k, v)

Test:

```python
c = Config()
c.create('123')

J = c.export_json()
print('Serialized:')
print(json.dumps(J, indent=4))

d = Config()
d.construct_from_json(J)
print('Reconstructed: w.b = ', d.w.b)
 

Выход:

 Serialized:
{
    "foo": "123",
    "bar": "Hello 123",
    "v": {
        "b": 2
    },
    "w": {
        "b": 2
    }
}
Reconstructed: w.b =  2
 

Однако существует ли предпочтительный/питонический способ сделать это?

Комментарии:

1. вы уже разобрались pickle ?

Ответ №1:

Как кто-то упоминал в комментариях, вы можете просто захотеть использовать pickle библиотеку здесь, чтобы избежать необходимости сериализации/десериализации самостоятельно и избежать необходимости вносить серьезные изменения в код сериализации в будущем, если вы добавляете вложенные структуры / и т.д. Или хотите игнорировать другие атрибуты. Вот версия вашего кода , которая работает с pickle атрибутом, но не сериализует secret его.

 class Empty:
    pass

class Config:
    def __init__(self):
        # don't want to transmit this over the internet
        self.secret = 'P@ssw0rd'

    def create(self, foo):
        self.foo = foo  # property passed in
        self.bar = f'Hello {foo}'  # calculated property

        # A couple of custom objects, but they are simple
        # (only containing key/value pairs where value is basic primitive)
        self.v = Empty()
        self.v.a = 1

        self.w = Empty()
        self.w.b = 2
    
    # This gets the state for pickling. Note how we are explicitly removing
    # the `secret` attribute from the internal dictionary. You don't need to
    # do anything else
    def __getstate__(self):
        state = self.__dict__.copy()
        del state['secret']
        return state
 

Проверяю это:

 import pickle
c = Config()
c.create('123')

J = pickle.dumps(c)
print("Serialized: ", J)

d = pickle.loads(J)
print("Reconstructed w.b:", d.w.b)
print("Reconstructed secret:", d.secret)
 

И это результат, который он производит (по желанию):

 Serialized:  b'x80x04x95mx00...(truncated)'
Reconstructed w.b: 2
Traceback (most recent call last):
  File "/Users/mustafa/scratch/test.py", line 36, in <module>
    print("Reconstructed secret:", d.secret)
AttributeError: 'Config' object has no attribute 'secret'
 

Комментарии:

1. Если вам необходимо использовать JSON, вы можете просто попробовать сделать json.dumps(state) (где state то же самое, что и в приведенном выше коде), но это было бы более раздражающим, так как вам нужно снова явно обрабатывать вложенные значения.