@lorem ipsum 的方法应该可以使用我自己没有尝试使用 swiftUI,但是处理 2 种不同类型的对象感觉有点复杂。尽管它们共享一个通用协议,但由于将被解码的是同一个对象,因此跟踪一个单一类型似乎很自然。
正如@Larme 所述,它可以使用自定义init(from decoder: Decoder)
方法来完成。
import UIKit
let jsonA = """
[
{
"id": 45,
"chapter__book__name": "Alonso",
"chapter__book__id": 70,
"chapter__chapter": 2,
"verse": "",
"verse_number": 5,
"chapter": 97
},
]
"""
let jsonB = """
[
{
"id": 962,
"book_name": "Title here",
"book_id": 70,
"chapter_number": 32,
"verse": "xxx",
"verse_number": 24,
"chapter": 127
},
]
"""
protocol VerseCodingKey: CodingKey {
static var id: Self { get }
static var book_name: Self { get }
static var book_id: Self { get }
static var verse: Self { get }
static var verse_number: Self { get }
static var chapter: Self { get }
static var chapter_number: Self { get }
}
struct Verse: Decodable {
var id: Int
var book_name: String
var book_id: Int
var verse: String
var verse_number: Int
var chapter: Int
var chapter_number: Int
enum CodingKeysA: String, VerseCodingKey {
case id
case book_name
case book_id
case verse
case verse_number
case chapter
case chapter_number
}
enum CodingKeysB: String, VerseCodingKey {
case id
case book_name = "chapter__book__name"
case book_id = "chapter__book__id"
case verse
case verse_number
case chapter = "chapter__chapter"
case chapter_number = "chapter"
}
init(from decoder: Decoder) throws {
do {
try self.init(from: decoder, verseCodingKey: CodingKeysA.self)
return
} catch { }
do {
try self.init(from: decoder, verseCodingKey: CodingKeysB.self)
return
} catch { }
throw CustomError.unmatchedCodingKeys
}
init<T: VerseCodingKey>(from decoder: Decoder, verseCodingKey: T.Type) throws {
do {
let values = try decoder.container(keyedBy: T.self)
id = try values.decode(Int.self, forKey: .id)
book_name = try values.decode(String.self, forKey: .book_name)
book_id = try values.decode(Int.self, forKey: .book_id)
verse = try values.decode(String.self, forKey: .verse)
verse_number = try values.decode(Int.self, forKey: .verse_number)
chapter = try values.decode(Int.self, forKey: .chapter)
chapter_number = try values.decode(Int.self, forKey: .chapter_number)
} catch {
throw CustomError.missingCodingKey
}
}
}
enum CustomError: Error {
case missingCodingKey
case unmatchedCodingKeys
}
let dataA = jsonA.data(using: .utf8)!
let dataB = jsonB.data(using: .utf8)!
let verseA = try? JSONDecoder().decode([Verse].self, from: dataA)
let verseB = try? JSONDecoder().decode([Verse].self, from: dataB)
此代码适用于游乐场
旁注:
关键是要处理两个不同CodingKey
的 s。
由于这种演变,现在使枚举符合协议是可行的,在深入探讨您的问题之前,我现在没有这样做。这使得代码更加直接和可重用。
可能有更好的方法来处理该do catch
机制,但在这一点上是可以接受的。正如@Cristik 在评论中所说,您应该增强错误处理机制,因为您不想让所有错误都通过。见下面他的评论
这就是我在这个小实验中能走多远,我认为有人能做得更好。使用单个具体类而不是两个加一个协议似乎仍然更可靠,但同样,我不是假装是专家。