序列化/反序列化简单但嵌套的对象到/从 JSON

问题描述

我正在尝试将配置从客户端传输到服务器。

  • 配置包含一个 password 密钥,我不能传输
  • Config 包含几个简单的对象,它们只是键/值对(值是基本原语)

此代码有效:

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

但是,有没有一种首选/pythonic 的方法来做到这一点?

解决方法

正如有人在评论中提到的那样,您可能只想在这里使用 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'\x80\x04\x95m\x00...(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:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...