現在、次のような JSON をデコードしようとしています。
{
"result": {
"success": true,
"items": [
{
"timeEntryID": "1",
"start": "1519558200",
"end": "1519563600",
"customerName": "Test-Customer",
"projectName": "Test-Project",
"description": "Entry 1",
},
{
"timeEntryID": "2",
"start": "1519558200",
"end": "1519563600",
"customerName": "Test-Customer",
"projectName": "Test-Project",
"description": "Entry 2",
}
],
"total": "2"
},
"id": "1"
}
この特定のタイプの JSON のデコード プロセスは非常に単純です。私はちょうどこのようなものが必要です:
struct ResponseKeys: Decodable {
let result: ResultKeys
struct ResultKeys: Decodable {
let success: Bool
let items: [Item]
}
}
今私が直面している問題は、サーバーのすべての応答が上記の JSON と同じ構造を持っているが、アイテムの種類が異なることです。そのため、そうである場合もありますが、ユーザー エンドポイントを呼び出した場合let items: [Item]
もそうなる可能性があります。let items: [User]
アイテム配列を変更するだけですべてのエンドポイントに対して上記の迅速なコードを記述すると、コードの不要な複製になるため、カスタム デコーダーを作成しました。
enum KimaiAPIResponseKeys: String, CodingKey {
case result
enum KimaiResultKeys: String, CodingKey {
case success
case items
}
}
struct Activity: Codable {
let id: Int
let description: String?
let customerName: String
let projectName: String
let startDateTime: Date
let endDateTime: Date
enum CodingKeys: String, CodingKey {
case id = "timeEntryID"
case description
case customerName
case projectName
case startDateTime = "start"
case endDateTime = "end"
}
}
extension Activity {
init(from decoder: Decoder) throws {
let resultContainer = try decoder.container(keyedBy: KimaiAPIResponseKeys.self)
let itemsContainer = try resultContainer.nestedContainer(keyedBy: KimaiAPIResponseKeys.KimaiResultKeys.self, forKey: .result)
let activityContainer = try itemsContainer.nestedContainer(keyedBy: Activity.CodingKeys.self, forKey: .items)
id = Int(try activityContainer.decode(String.self, forKey: .id))!
description = try activityContainer.decodeIfPresent(String.self, forKey: .description)
customerName = try activityContainer.decode(String.self, forKey: .customerName)
projectName = try activityContainer.decode(String.self, forKey: .projectName)
startDateTime = Date(timeIntervalSince1970: Double(try activityContainer.decode(String.self, forKey: .startDateTime))!)
endDateTime = Date(timeIntervalSince1970: Double(try activityContainer.decode(String.self, forKey: .endDateTime))!)
}
}
"items"
配列ではなく単一のオブジェクトのみが含まれている場合、デコーダーは完全に機能します。
{
"result": {
"success": true,
"items":
{
"timeEntryID": "2",
"start": "1519558200",
"end": "1519563600",
"customerName": "Test-Customer",
"projectName": "Test-Project",
"description": "Entry 2",
},
"total": "2"
},
"id": "1"
}
が配列の場合items
、次のエラーが発生します。
typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [__lldb_expr_151.KimaiAPIResponseKeys.result], debugDescription: "Dictionary をデコードすると予想されますが、代わりに配列が見つかりました。", 基本エラー: nil))
デコーダーを変更してアイテムの配列を操作する方法がわかりません。機能するバージョンと機能しないバージョンの JSON を使用して Playground ファイルを作成しました。見て、試してみてください: Decodable.playground
ご協力ありがとうございました!