【问题标题】:Datatype conversion using Python Marshmallow使用 Python Marshmallow 进行数据类型转换
【发布时间】:2020-05-07 14:52:08
【问题描述】:

我正在尝试使用 Marshmallow 模式来序列化 python 对象。下面是我为我的数据定义的架构。

from marshmallow import Schema, fields

class User:

    def __init__(self, name = None, age = None, is_active = None, details = None):
        self.name = name
        self.age = age
        self.is_active = is_active
        self.details = details

class UserSchema(Schema):
    name = fields.Str()
    age = fields.Int()
    is_active = fields.Bool()
    details = fields.Dict()

输入将是字典格式,所有值都将是字符串。

user_data = {"name":"xyz", "age":"20", "is_active": 'true',"details":"{'key1':'val1', 'key2':'val2'}"}

当我尝试运行下面的 sn-p 时,ageis_active 的值被转换为各自的数据类型,但 details 保持不变。

user_schema = UserSchema()
user_dump_data = user_schema.dump(user_data)
print(user_dump_data)

输出:

{'name': 'xyz', 'is_active': True, 'details': "{'key1':'val1', 'key2':'val2'}", 'age': 20}

我需要将输入数据序列化为我在架构中定义的相应数据类型。有什么我做错了吗?谁能指导我如何使用 Marshmallow 实现这一目标?

我正在使用

python 3.6
marshmallow 3.5.1

编辑

上述输入数据是从 HBase 中获取的。默认情况下,HBase 将其所有值存储为字节并以字节返回。下面是我从 HBase 得到的格式

{b'name': b'xyz', b'age': b'20', b'is_active': b'true', b'details': b"{'key1':'val1', 'key2':'val2'}"}

然后我解码此字典并将其传递给我的 UserSchema 以将其序列化以在 Web API 中使用。

【问题讨论】:

    标签: python python-3.x marshmallow


    【解决方案1】:

    您混淆了序列化(转储)和反序列化(加载)。

    转储正在从对象形式转为 json 可序列化的基本 python 类型(使用 Schema.dump)或 json 字符串(使用 Schema.dumps)。加载是相反的操作。

    通常,您的 API 从外部加载(和验证)数据,并将您的对象转储(未经验证)到外部。

    如果你的输入数据就是这个数据,并且你想将它加载到对象中,你需要使用load,而不是dump

    user_data = {"name":"xyz", "age":"20", "is_active": 'true',"details":"{'key1':'val1', 'key2':'val2'}"}
    user_loaded_data = user_schema.load(user_data)
    user = User(**user_loaded_data)
    

    除非你这样做,否则你会遇到另一个问题。 DictField 期望数据为 dict,而不是 str。您需要输入

    user_data = {"name":"xyz", "age":"20", "is_active": 'true',"details": {'key1':'val1', 'key2':'val2'}}
    

    【讨论】:

    • 感谢您的回复。是的,你是对的,我可以使用负载,但我正在尝试将序列化数据返回到 Web 应用程序。我已经编辑了问题
    • 使用 dump 将带有架构的对象转储到您的 API。要使用 BD 数据构建对象,您可以使用 schema.load,但通常,您会使用数据访问对象或类似 ORM 的层(如 sql-alchemy)。
    • 如果直接从 DB 转到 API,则首先不需要对象。
    【解决方案2】:

    正如 Jérôme 所说,您将序列化(转储)与反序列化(加载)混淆了。根据您的要求,您应该按照建议使用Schema.load

    因此,所有输入值都应为字符串类型。您可以使用pre_load注册一个预处理数据的方法,如下所示:

    from marshmallow import Schema, fields, pre_load
    
    class UserSchema(Schema):
        name = fields.Str()
        age = fields.Int()
        is_active = fields.Bool()
        details = fields.Dict()
    
        @pre_load
        def pre_process_details(self, data, **kwarg):
            data['details'] = eval(data['details'])
            return data
    
    user_data = {"name":"xyz", "age":"20", "is_active": 'true',"details":"{'key1':'val1', 'key2':'val2'}"}
    
    user_schema = UserSchema()
    user_loaded_data = user_schema.load(user_data)
    print(user_loaded_data)
    

    这里,pre_process_details 会将字符串类型转换为字典以进行正确的反序列化。

    【讨论】:

    • 在 API 中使用 eval 是一个危险信号。永远不要评估外部世界的数据。在数据库数据上使用它可能没有那么危险,但它看起来像是一个设计问题。
    • @Jérôme 感谢您强调这一点并告知我这个危险信号。我的意图是告知使用pre_load 如果输入是字符串类型。我们可以使用其他方式将字符串转换为字典,比如json.loads()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-06
    • 2022-01-26
    • 1970-01-01
    • 2019-01-15
    • 2011-06-21
    • 1970-01-01
    • 2020-10-20
    相关资源
    最近更新 更多