簡體   English   中英

使用 Swift 中的 Codable 解析 JSON 與相同 object 的不同鍵

[英]Parse JSON with different keys to same object using Codable in Swift

我收到來自不同 API 的以下 2 個響應

{
  "id": "jdu72bdj",
  "userInfo": {
    "name": "Sudhanshu",
    "age": 28,
    "country": "India"
  }
}

{
  "profileId": "jdu72bdj",
  "profileDetails": {
    "name": "Sudhanshu",
    "age": 28,
    "country": "India"
  }
}

這與使用Swift語言的iOS development有關。 基本上 object 結構相同,但鍵不同。 我正在使用Codable解析這些,但我想不出使用相同結構解析的方法。 我能想到的就是像這樣制作2個不同的結構 -

public struct Container1: Codable {
  public let id: String
  public let userInfo: UserProfile?    
}

public struct Container2: Codable {
  public let profileId: String
  public let profileDetails: UserProfile?    
}

他們都使用通用的UserProfile結構。

public struct UserProfile: Codable {
  public let name: String?
  public let age: Int?
  public let country: String?
}

有沒有一種方法可以為兩個響應使用一個通用容器結構來解析來自 2 個不同鍵的響應。 我不想要 Container1 和 Container2 因為它們都有相同的結構。

有什么建議么?

一種解決方案是使用自定義密鑰解碼策略,使用 Apple 文檔中的CodingKey實現。 這個想法是將 map 兩個 json 消息的鍵與將用於兩個消息的結構容器的屬性。

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

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

然后使用以下結構以相同的方式解碼兩個 json 消息

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

let result = try decoder.decode(Container.self, from: data)

您可以從解碼器中使用自己的 init

struct UniversalProfileContainer: Decodable {
    struct UserProfile: Decodable {
        var name: String
        var age: Int
        var country: String
    }
    
    enum CodingKeys: String, CodingKey {
        case id = "id"
        case profileId = "profileId"
        case userInfo = "userInfo"
        case profileDetails = "profileDetails"
    }

    let id: String
    let profile: UserProfile

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            id = try container.decode(String.self, forKey: .id)
        } catch {
            id = try container.decode(String.self, forKey: .profileId)
        }
        do {
            profile = try container.decode(UserProfile.self, forKey: .userInfo)
        } catch {
            profile = try container.decode(UserProfile.self, forKey: .profileDetails)
        }
    }
}

let first = """
{
  "id": "jdu72bdj",
  "userInfo": {
    "name": "Sudhanshu",
    "age": 28,
    "country": "India"
  }
}
"""
let second = """
{
  "profileId": "jdu72bdj",
  "profileDetails": {
    "name": "Sudhanshu",
    "age": 28,
    "country": "India"
  }
}
"""
let response1 = try? JSONDecoder().decode(UniversalProfileContainer.self,
                                            from: first.data(using: .utf8)!)

let response2 = try? JSONDecoder().decode(UniversalProfileContainer.self,
                                            from: second.data(using: .utf8)!)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM