将JSON解码为Swift中的泛型数组或类

myzjeezk  于 2023-06-04  发布在  Swift
关注(0)|答案(4)|浏览(492)

如何在swift中将json解码为通用模型?
在java中,我使用GSON来解码json,一般情况下,我使用<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>>
谢谢。

k97glaaz

k97glaaz1#

如果你声明了一个Decodable属性和json中的键同名,那么你并不需要一个enum来定义Coding键,也不需要一个初始化器来手动Map每个属性和键。
此外,在Swift中没有必要继承NSObject,直到有了特定的用例。查看声明,似乎没有必要,因此可以像这样简单地重新声明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)
    }
}

你必须使用下面的方法来声明数组和字典

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]]
ercv8c1e

ercv8c1e2#

这里有一个你可能想用来解码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

hrysbysz

hrysbysz3#

如果T符合Decodable,则Array<T>符合Decodable,因此GeneralResponse<[ItemDemoModel]>不会产生任何错误。
如下所示:

您可以简单地这样做:

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

umuewwlo4#

如果你在json中声明了一个Decodable属性,并且和key同名,那么你并不需要一个枚举来定义Coding key,也不需要一个初始化器来手动Map每个属性和key。
此外,在Swift中没有必要从NSObject继承,直到你有一个特定的用例。查看声明,似乎没有必要,因此您的GeneralResponse可以像这样简单地重新声明,
struct ResponseModel<T: Decodable>:Decodable {

var errorCode: Int?
var body: T?
var message: String?
var success: Bool?

}

如果您使用的键与响应不同

struct ResponseModel<T: Decodable>:Decodable {

var errorCode: Int?
var body: T?
var message: String?
var success: Bool?

enum CodingKeys:String,String {

case errorCode
    case body
    case message = "response_Message"
    case success
}

}

相关问题