【问题标题】:(Swift) Parse JSON with JSONDecoder that has an object that changes structure depending on message [duplicate](Swift)使用 JSONDecoder 解析 JSON,该对象具有根据消息更改结构的对象 [重复]
【发布时间】:2019-02-14 22:59:06
【问题描述】:

问题基本上归结于此。我的应用正在接收这种 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))

当然因为 Any 这不能符合 Codable。我当然可以只手动提取动作字段,创建动作映射(枚举)到结构类型,然后执行JSONDecoder().decode(self.commandMap[ActionKey], data: jsonData)。此解决方案可能还需要对适当的结构类型进行一些转换,以便在解析后使用对象。 但也许有人有更好的方法?所以这个类不是300行吗?任何想法都非常感谢。

【问题讨论】:

  • 不,只有 10 条消息,但希望避免使用 blob 结构进行解析,不过感谢您的建议。
  • 基本上,您只需使用自定义初始化,以便您可以解码action 并使用它的值告诉您使用什么结构来解码data。查看链接的问题和答案。
  • @matt 我不确定它是否完全重复。这与未知键无关,因此您不需要在此处自定义CodingKeys
  • @Sulthan 如果你愿意,你可以取消重复,那是你的特权;我不会生气的。但在我看来,“我如何根据第一个键的值对第二个键进行不同的处理”这个问题已经在 SO 上得到了完全的回答。

标签: json swift decode


【解决方案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 转换为您需要的类型。 这可以通过将 actiondata 合并到一个带有关联对象的枚举中来缓解,例如:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-05
    • 1970-01-01
    • 2020-05-20
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 1970-01-01
    相关资源
    最近更新 更多