问题基本上可以归结为这一点。我的应用正在接收以下JSON格式的消息:
{
"action": "ready",
"data": null
}
或
{
"action": "error",
"data": {
"code": String,
"exception": String,
"status": Int
}
}
或
{
"action": "messageRequest",
"data": {
"recipientUserId": String,
"platform": String,
"content": String
}
}
或
{
"action": "tabsChange",
"data": {
"roomsTabs": [{
"configuration": {
"accessToken": STRING,
"url": STRING,
"userId": STRING
},
"id": STRING,
"isOnline": BOOLEAN,
"isUnread": BOOLEAN,
"lastActive": NUMBER,
"name": STRING,
"participantBanned": BOOLEAN,
"platform": STRING,
"secondParticipant": {
"id": STRING,
"platform": STRING,
"userId": STRING
},
"secondParticipantId": STRING,
"state": STRING,
"unreadMessages": NUMBER
]}
}
}
如您所见,数据对象具有不同的结构,具体取决于消息,并且消息对象可能变大(其中有10个以上)。 我不想手工分析所有内容,逐场分析,理想的解决方案当然是:
struct ChatJsCommand: Codable {
let action: String
let data: Any?
}
self.jsonDecoder.decode(ChatJsCommand.self, from: jsonData))
当然由于任何原因,这都不符合Codable。当然,我只能手动提取动作字段,创建动作映射(枚举)以构造结构类型,然后执行JSONDecoder().decode(self.commandMap[ActionKey], data: jsonData)
。此解决方案可能还需要一些转换为适当的结构类型,才能在解析后使用这些对象。
但是也许有人有更好的方法?那班不是300行吗?任何想法都非常感谢。
答案 0 :(得分:1)
让我们从定义数据协议开始,它可以是一个空协议。以后对我们有帮助:
protocol MessageData: Decodable {
}
现在让我们准备数据对象:
struct ErrorData: MessageData {
let code: String
let exception: String
let status: Int
}
struct MessageRequestData: MessageData {
let recipientUserId: String
let platform: String
let content: String
}
(在需要时使用可选选项)
现在,我们还必须知道数据类型:
enum ActionType: String {
case ready
case error
case messageRequest
}
现在最困难的部分:
struct Response: Decodable {
let action: ActionType
let data: MessageData?
private enum CodingKeys: String, CodingKey {
case action
case data
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let action = try values.decode(ActionType.self, forKey: .action)
self.action = action
switch action {
case .ready:
data = nil
case .messageRequest:
data = try values.decode(MessageRequestData.self, forKey: .data)
case .error:
data = try values.decode(ErrorData.self, forKey: .data)
}
}
}
唯一的技巧是首先解码action
,然后根据内部值进行解析。唯一的问题是,在使用Response
时,始终必须先检查action
,然后将data
强制转换为所需的类型。
可以通过将action
和data
合并到一个与关联对象(例如对象)的枚举中来缓解这种情况。 :
enum Action {
case ready
case error(ErrorData)
case messageRequest(MessageRequestData)
case unknown
}
struct Response: Decodable {
let action: Action
private enum CodingKeys: String, CodingKey {
case action
case data
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let actionString = try values.decode(String.self, forKey: .action)
switch actionString {
case "ready":
action = .ready
case "messageRequest":
let data = try values.decode(MessageRequestData.self, forKey: .data)
action = .messageRequest(data)
case "error":
let data = try values.decode(ErrorData.self, forKey: .data)
action = .error(data)
default:
action = .unknown
}
}
}