编写多个 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 问题:
Multiple try codes in one block
但它们似乎并不是实现我想要的目标的最佳方式。
由于每种情况在语法上都是相同的,因此您可以在一个循环中处理它们。遍历一系列案例和 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}')
虽然 可以按要求解决您的问题,但如果您一开始就不需要 "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
我有一个函数可以接收多个具有不同结构 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 问题:
Multiple try codes in one block
但它们似乎并不是实现我想要的目标的最佳方式。
由于每种情况在语法上都是相同的,因此您可以在一个循环中处理它们。遍历一系列案例和 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}')
虽然
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