Swift - 将类型动态传递给 JSONDecoder
Posted
技术标签:
【中文标题】Swift - 将类型动态传递给 JSONDecoder【英文标题】:Swift - passing types dynamically to JSONDecoder 【发布时间】:2018-07-27 11:38:52 【问题描述】:我正在尝试使用通用嵌套对象从 json 对象中解码,为此我想在解码时动态传递类的类型。
例如,我的类是扩展 ObjectModel(和 :Codable)的 EContactModel 和 ENotificationModel。 ENotificationModel 可以包含嵌套的 ObjectModel(可以是联系人、通知或其他对象模型)。
我有一本这样的字典:
static let OBJECT_STRING_CLASS_MAP = [
"EContactModel" : EContactModel.self,
"ENotificationModel" : ENotificationModel.self
...
]
我在 ENotificationModel 中的解码初始化方法如下所示:
required init(from decoder: Decoder) throws
try super.init(from: decoder)
let values = try decoder.container(keyedBy: CodingKeys.self)
...
//decode some fields here
self.message = try values.decodeIfPresent(String.self, forKey: .message)
...
//decode field "masterObject" of generic type ObjectModel
let cls = ObjectModelTypes.OBJECT_STRING_CLASS_MAP[classNameString]!
let t = type(of: cls)
print(cls) //this prints "EContactModel"
self.masterObject = try values.decodeIfPresent(cls, forKey: .masterObject)
print(t) //prints ObjectModel.Type
print(type(of: self.masterObject!)) //prints ObjectModel
我也尝试过传递 type(of: anObjectInstanceFromADictionary) 但仍然无法正常工作,但如果我传递 type(of: EContactModel()) 则可以。我无法理解这一点,因为两个对象都是相同的(即 EContactModel 的实例)
有解决办法吗?
【问题讨论】:
【参考方案1】:您可以使用可选变量声明您的对象模型,并让 JSONDecoder 为您解决问题。
class ApiModelImage: Decodable
let file: String
let thumbnail_file: String
...
class ApiModelVideo: Decodable
let thumbnail: URL
let duration: String?
let youtube_id: String
let youtube_url: URL
...
class ApiModelMessage: Decodable
let title: String
let body: String
let image: ApiModelImage?
let video: ApiModelVideo?
...
那么你所要做的就是……
if let message = try? JSONDecoder().decode(ApiModelMessage.self, from: data)
if let image = message.image
print("yay, my message contains an image!")
if let video = message.video
print("yay, my message contains a video!")
或者,您可以使用泛型并在调用 API 代码时指定类型:
func get<T: Decodable>(from endpoint: String, onError: @escaping(_: Error?) -> Void, onSuccess: @escaping (_: T) -> Void)
getData(from: endpoint, onError: onError) (data) in
do
let response = try JSONDecoder().decode(T.self, from: data)
onSuccess(response)
catch
onError(error)
使用这种方式,您只需确保定义了预期的响应类型:
let successCb = (_ response: GetUnreadCountsResponse) in
...
ApiRequest().get(from: endpoint, onError: (_) in
...
, onSuccess: successCb)
由于您将 successCb 定义为需要 GetUnreadCountsResponse 模型,因此 API get 方法泛型在运行时将是 GetUnreadCountsResponse 类型。
祝你好运!
【讨论】:
好的,但这不是我的情况。在我的模型中,我有很多扩展 ObjectModel 的实体。所以我的字段类型是通用的。我不能为每种对象类型设置一个字段。在您的示例中,我需要的是一个字段,比如说 Decodable 类型的“媒体”(或扩展 Decodable 的类型),然后动态解码该字段而无需 switch/if 感谢您的回答。问题是当尝试在 init(from: decoder) 方法中应用相同的逻辑时,返回的类型是超类的类型,而不是实际的类。以上是关于Swift - 将类型动态传递给 JSONDecoder的主要内容,如果未能解决你的问题,请参考以下文章
Swift:如何将可编码类型作为函数输入传递给编码或解码 json
Swift 3:如何动态加载图像并将变量传递给第二个视图控制器?