декодирование JSON в общий массив или класс в Swift - PullRequest
0 голосов
/ 19 декабря 2018

Как вы декодируете json в обобщенную модель в swift?
В java для декодирования json я использую GSON, и в целом не имеет значения, что я использую <T<E>> or ArrayList<E>. В swift Array является структурой и не может бытьнаследование, и оно не реализовано Decodable.

Я ищу общий элегантный класс для использования во всех моих веб-сервисах.

Мой сценарий:
У меня есть ответ json

{
"status": true,
"message": "",
"code": 200,
"response": [{
    "id": 43
}]
}

и общая модель ответа, подобная этой, от веб-сервисов:

class GeneralResponse< T : Decodable >:NSObject,Decodable{

    var status = false
    var message = ""
    var code = -1
    var response : T?

    private enum CodingKeys: String, CodingKey {
        case status
        case message
        case code
        case response
    }

    required public init(from decoder: Decoder) throws{
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decode(Bool.self, forKey: .status)
        message = try container.decode(String.self, forKey: .message)
        code = try container.decode(Int.self, forKey: .code)
        response = try container.decode(T.self, forKey: .response)
    }

}
class ItemDemoModel:Decodable {
     var id = -1
    private enum ItemDemModelCodingKeys : String, CodingKey {
        case id
    }
     required init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy: ItemDemModelCodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
    }
}

переменная ответа может быть ItemDemoModelили массив ItemDemoModel.
Например:
Это может быть GeneralResponse<Array<ItemDemoModel>>> или GeneralResponse<ItemDemoModel>>

спасибо.

Ответы [ 3 ]

0 голосов
/ 19 декабря 2018

Если вы объявляете Decodable свойства с тем же именем, что и ключ в json, тогда вам не нужно enum для определения Coding ключей и инициализатор для ручного сопоставления каждого свойства с ключом.

Кроме того, нет необходимости наследовать от NSObject в Swift, пока у вас не будет конкретного варианта использования для этого.Глядя на объявление, оно кажется ненужным, поэтому ваш GeneralResponse может быть переопределен так просто:

class GeneralResponse<T: Decodable>: Decodable {

    var code: Int
    var status: Bool
    var message: String?
    var response : T?
}

Аналогично, ItemDemoModel может быть объявлен следующим образом:

class ItemDemoModel: Decodable {
     var id: Int
}

Теперь вы можете настроить свой сервис, как показано ниже, чтобы получить GeneralResponse<T> для любого запроса,

struct RequestObject {
    var method: String
    var path: String
    var params: [String: Any]
}

class WebService {

    private let decoder: JSONDecoder

    public init(_ decoder: JSONDecoder = JSONDecoder()) {
        self.decoder = decoder
    }

    public func decoded<T: Decodable>(_ objectType: T.Type,
                                      with request: RequestObject,
                                      completion: @escaping  (GeneralResponse<T>?, Error?) -> Void)  {
        // Here you should get data from the network call. 
        // For compilation, we can create an empty object.
        let data = Data()

        // Now parsing
        do {
            let response  = try self.decoder.decode(GeneralResponse<T>.self, from: data)
            completion(response, nil)
        } catch {
            completion(nil, error)
        }
    }
}

Использование

let request = RequestObject(method: "GET", path: "https://url.com", params: [:])
WebService().decoded([ItemDemoModel].self, with: request) { (response, error) in
    if let items = response?.response {
        print(items)
    }
}

PS;Вы должны быть использованы для объявления массивов и словарей, как показано ниже:

let array: Array<SomeType>
let dictionary: Dictionary<String: SomeType>
let arrayOfDictionary: Array<Dictionary<String: SomeType>>

Но с помощью Swift вывода типа вы можете объявить array и dictionary так же просто, как показано ниже,

let array: [SomeType]
let dictionary: [String: SomeType]
let arrayOfDictionary: [[String: SomeType]]
0 голосов
/ 27 декабря 2018

Здесь у вас есть функция, которую вы можете использовать для декодирования вашего JSON:

func decode<T: Decodable>(_ data: Data, completion: @escaping ((T) -> Void)) {
    do {
        let model = try JSONDecoder().decode(T.self, from: data)
        completion(model)
    } catch {
        log(error.localizedDescription, level: .error)
    }
}

Так что вы можете просто вызвать свою функцию следующим образом:

decode(data, completion: { (user: User) in
            // Do something with your parsed user struct or whatever you wanna parse
        })

Я надеюсь, что этопомогает: D

0 голосов
/ 19 декабря 2018

Array<T> соответствует Decodable, если T соответствует Decodable, поэтому GeneralResponse<[ItemDemoModel]> не вызовет никаких ошибок.

Как показано здесь:

enter image description here

Вы можете просто сделать это:

let decoder = JSONDecoder()
let obj = try decoder.decode(type, from: json.data(using: .utf8)!)
...