使用 Swift 中的 Codable 将具有不同键的 JSON 解析为同一对象

che*_*oot 3 parsing json ios swift codable

我收到来自不同 API 的以下 2 个响应

{
  "id": "jdu72bdj",
  "userInfo": {
    "name": "Sudhanshu",
    "age": 28,
    "country": "India"
  }
}
Run Code Online (Sandbox Code Playgroud)

{
  "profileId": "jdu72bdj",
  "profileDetails": {
    "name": "Sudhanshu",
    "age": 28,
    "country": "India"
  }
}
Run Code Online (Sandbox Code Playgroud)

iOS development这是与使用语言相关的Swift。基本上对象结构是相同的,但键不同。我正在使用 解析这些Codable,但我想不出一种使用相同结构进行解析的方法。我能想到的就是制作两种不同的结构,如下所示 -

public struct Container1: Codable {
  public let id: String
  public let userInfo: UserProfile?    
}
Run Code Online (Sandbox Code Playgroud)

public struct Container2: Codable {
  public let profileId: String
  public let profileDetails: UserProfile?    
}
Run Code Online (Sandbox Code Playgroud)

他们都使用共同的UserProfile结构。

public struct UserProfile: Codable {
  public let name: String?
  public let age: Int?
  public let country: String?
}
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以为两个响应使用一个公共容器结构,从而解析来自两个不同键的响应。我不想要 Container1 和 Container2,因为它们具有相同的结构。

有什么建议 ?

Joa*_*son 5

一种解决方案是使用自定义密钥解码策略,该策略使用CodingKeyApple文档中找到的实现。这个想法是将两个 json 消息的键映射到将用于这两个消息的 struct Container 的属性。

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({ keys in
    let key = keys.last!.stringValue
    switch key {
    case "id", "profileId":
        return AnyKey(stringValue: "id")!
    case "userInfo", "profileDetails":
        return AnyKey(stringValue: "details")!
    default:
        return keys.last!
    }        
})
Run Code Online (Sandbox Code Playgroud)

其中的自定义实现CodingKey

struct AnyKey: CodingKey {
    var stringValue: String
    var intValue: Int?

    init?(stringValue: String) {
        print(stringValue)
        self.stringValue = stringValue
        intValue = nil
    }

    init?(intValue: Int) {
        self.stringValue = String(intValue)
        self.intValue = intValue
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用以下结构以相同的方式解码两个 json 消息

struct Container: Codable {
    let id: String
    let details: UserProfile
}

let result = try decoder.decode(Container.self, from: data)
Run Code Online (Sandbox Code Playgroud)

  • @Larme `CodingKey` 协议需要处理 String 和 Int (2认同)