【问题标题】:What is a better way to write multiple try clauses for casting a json string object to a dataclass?编写多个 try 子句以将 json 字符串对象转换为数据类的更好方法是什么?
【发布时间】:2020-05-03 01:55:26
【问题描述】:

我有一个函数可以接收多个具有不同结构和/或字段名称的不同 json 字符串对象,如下所示:

event = '{"userId": "TDQIQb2fQaORKvCyepDYoZgsoEE3", "profileIsCreated": true}'

或

event = '{"userId": "TDQIQb2fQaORKvCyepDYoZgsoEE3", "signUpFinished": true}'

我有这样的数据类:

from dataclasses import dataclass, field


@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass(frozen=True)
class UserId:
    userId: str


@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass(frozen=True)
class SignUpFinished(UserId):
    signUpFinished: bool


@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass(frozen=True)
class UserProfileCreated(UserId):
    profileIsCreated: bool

目前,我编写函数的方式是这样的:

def cast_event(event):
    user_details = None

    try:
        user_details = SignUpFinished.from_json(event)
    except KeyError:
        pass

    try:
        user_details = UserProfileCreated.from_json(event)
    except KeyError:
        pass

    if user_details:
        return "OK"
    else:
        return "UNHANDLED"

问题是,随着我要处理的事件越来越多,我的函数会变得越来越长,然而,它只是做同样的事情。

有没有更好的方法来实现我想要实现的目标?


我已经检查了一些 SO 问题:

但它们似乎不是实现我想要的最佳方式。

【问题讨论】:

  • 我建议尝试在这里进行更正式的区分,例如 {"event": "signupFinished", "data": {...}},这样您就可以轻松地打开 event 键来确定您正在处理的数据结构.暴力破解似乎是一种不好的方法,如果两个不同的数据类可能能够反序列化相同的 JSON,将会变得混乱。
  • 我能确认你的意思吗?因此,当将event 发送到函数cast_event 时,我应该像您展示的那样将事件类型添加为有效负载中的一个字段?实际事件将在data?所以,我想我会喜欢查找dict 或使用正确数据类的东西?
  • 是的,完全正确。设置它,以便您可以将一个值映射到特定的数据类,而不是基本上需要将整个消息作为一个整体来确定它是什么数据类。
  • 你能用一些工作代码发布答案吗?

标签: python json python-3.x try-except python-dataclasses


【解决方案1】:

由于每种情况在语法上都是相同的,因此您可以在一个循环中处理它们。遍历一系列案例和try到return;这会自动继续尝试以后的案例,直到成功为止。

def cast_event(event):
    for case in (UserId , SignUpFinished, UserProfileCreated):
        try:
            return case.from_json(event)
        except KeyError:
            pass
    raise ValueError(f'not a valid event: {event}')

【讨论】:

    【解决方案2】:

    虽然loop approach 可以按要求解决您的问题,但如果您首先不需要“蛮力”方法来反序列化数据,那会好很多。为此,您需要一个明确地帮助您确定您正在处理的数据结构类型的字段。例如:

    event = {'event': 'profile',
             'data': {'userId': 'TDQIQb2fQaORKvCyepDYoZgsoEE3', 'profileIsCreated': True}}
    

    这里的事件 'profile' 将始终跟随一个带有键 'userId' 和 'profileIsCreated' 的对象。这是您的事件消息应该做出的保证,然后解析它们就很简单了:

    event_map = {
        'profile': UserProfileCreated,
        ...
    }
    
    return event_map[event['event']](**event['data'])
    

    请注意,我在这里跳过了 JSON 解析步骤。您需要先解析 JSON 以评估其 event 键,因此使用 dataclass_json 可能是多余的/没有用处。

    【讨论】:

    • 能否将解析json的部分添加到dataclass?我不确定我是否完全理解你的建议。
    • 最后一行是将解析的 JSON 数据传递给数据类构造函数的位置。此处省略了实际的 JSON 解析,但只是对某处的 json.load 的简单调用。
    【解决方案3】:

    对于指定的源数据,可以这样做:

    import json
    
    data = '{"userId": "TDQIQb2fQaORKvCyepDYoZgsoEE3", "profileIsCreated": true}'
    
    data = json.loads(data)
    
    user_id = data.pop('userId')
    user_details_key = list(data.keys())[0] if data else None
    user_details = list(data.values())[0] if data else None
    
    assert user_id == 'TDQIQb2fQaORKvCyepDYoZgsoEE3'
    assert user_details_key == 'profileIsCreated'
    assert user_details == True
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-14
      • 2017-02-14
      相关资源
      最近更新 更多