快速将json解码为通用数组或类

时间:2018-12-19 02:23:36

标签: json swift generics response decode

如何快速将json解码为通用模型?
在用于解码json的Java中,我使用GSON,一般而言,我使用<T<E>> or ArrayList<E>都没关系。在swift Array中,它是一个结构,不能继承,并且未实现Decodable。

我正在寻找可用于我的所有Web服务的通用优雅类。

我的情况:
我有json响应

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

和来自Web服务的通用响应模型:

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 :(得分:2)

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)!)

答案 1 :(得分:1)

如果您声明与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)
    }
}

P.S;您必须习惯于如下声明数组和字典,

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

但是使用Swift的类型推断,可以声明一个arraydictionary,如下所示,简单,

let array: [SomeType]
let dictionary: [String: SomeType]
let arrayOfDictionary: [[String: SomeType]]

答案 2 :(得分:0)

这里您可能要使用一个函数来解码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