用一个结构解码两个不同的 JSON 响应? [复制]
Posted
技术标签:
【中文标题】用一个结构解码两个不同的 JSON 响应? [复制]【英文标题】:Decoding two different JSON responses with one struct? [duplicate] 【发布时间】:2021-11-22 15:28:16 【问题描述】:我从两个端点接收到相同的 json 结构,唯一不同的是 json 中的键。在响应 #1 我得到
[
"id": 45,
"chapter__book__name": "Alonso",
"chapter__book__id": 70,
"chapter__chapter": 2,
"verse": "",
"verse_number": 5,
"chapter": 97
,
]
在响应 #2 中,我得到:
[
"id": 962,
"book_name": "Title here",
"book_id": 70,
"chapter_number": 32,
"verse": "xxx",
"verse_number": 24,
"chapter": 127
,
]
一个结构可以同时解码这两个吗?目前我的结构是这样的:
struct Verse: Decodable, Identifiable
let id: Int
let book_name: String
let book_id: Int
let verse: String
let verse_number: Int
let chapter: Int // chapter Id in database
let chapter_number: Int
匹配响应 #2,但不匹配响应 #1。
【问题讨论】:
可能有一个protocol
,但有两个`struts。您需要 2 组合并类型的编码键
如何使用 2 个结构,每个用于每个端点,并有第三个是“您的应用程序如何管理它们”?否则,您可以使用自定义 init(from: decoder)
,但这是一些工作。
【参考方案1】:
@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。
因为this evolution 现在可以使枚举符合协议,在深入研究您的问题之前我现在没有这样做。这使得代码更加直接和可重用。
可能有更好的方法来处理do catch
机制,但目前可以接受。正如@Cristik 在评论中所说,您应该增强错误处理机制,因为您不想让所有错误都通过。看看他下面的评论
这是我在这个小实验中能走多远,我认为有人能做得更好。使用单个具体类而不是两个加一个协议似乎仍然更可靠,但同样,我并不是在假装自己是专家。
【讨论】:
@Cristik 我没有得到你我必须说的包装部分。问题是,如果decode
方法抛出,基本上意味着已经使用了错误的codingKeys来尝试解码json,因此当它失败时,必须使用另一组Keys调用该方法。如果没有 return
s 语句,它总是会在最后抛出。我希望方法在解码成功时返回,如果我们到达 init 范围的末尾则抛出
@Cristik 我认为我们误会了。我的意思是,在这个init(from decoder: Decoder)
中,如果我去掉return
语句,它总是会到达不想要的throw CustomError.unmatchedCodingKeys
。我希望它在init(from decoder:, verseCodingKey:)
没有抛出时结束,这意味着解码成功。我知道do catch
是如何工作的,我向你保证哈哈
@Cristik 完全同意您在第二条评论中提到的wrap
部分的说法。这是概念证明,而不是要复制/粘贴的东西。策略就在这里,现在必须根据他的需求和项目结构对其进行调整和增强(以及一些好的做法,例如不绕过所有可能引发的错误)
@Cristik 不错!这是我把它扔掉的剩菜!谢谢以上是关于用一个结构解码两个不同的 JSON 响应? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
对 GMSCoordinateBounds 数组的 Swift 解码 JSON 响应