0

一般的なネストされたオブジェクトを使用して json オブジェクトからデコードしようとしています。このために、デコード時にクラスのタイプを動的に渡したいと考えています。

たとえば、私のクラスは ObjectModel (および :Codable) を拡張する EContactModel と ENotificationModel です。ENotificationModel には、ネストされた ObjectModel (連絡先、通知、またはその他のオブジェクトモデル) を含めることができます。

次のような型の辞書があります。

static let OBJECT_STRING_CLASS_MAP = [
        "EContactModel" : EContactModel.self,
        "ENotificationModel" : ENotificationModel.self
...
    ]

ENotificationModel のデコード init メソッドは次のようになります。

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 のインスタンス) であるため、これを理解できません。

これに対する解決策はありますか?

4

1 に答える 1

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 型になります。

幸運を!

于 2018-07-27T12:05:00.900 に答える