编写多个 try 子句以将 json 字符串对象转换为数据类的更好方法是什么?

What is a better way to write multiple try clauses for casting a json string object to a dataclass?

我有一个函数可以接收多个具有不同结构 and/or 字段名称的 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 问题:

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

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

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}')

虽然 可以按要求解决您的问题,但如果您一开始就不需要 "brute force" 方法来反序列化数据,那就更好了。为此,您需要一个明确帮助您确定正在处理的数据结构类型的字段。例如:

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 可能 superfluous/not 有用。

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

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