我的应用程序从我们的服务器接收二进制数据。在我的旧Objective-C代码中,我处理响应数据如下:
- 使用NSJSONSerialization将NSData转换为NSDictionary,其中包含以下数据:{quot;code":"0","info":{;user_id"123456}}
- 编写MTLModel子类
- 使用Mantle API将上述信息字典(即{"user_id":123456}(转换为我的模型
现在我想用Swift来做这件事,我刚刚学会了Codable协议有多方便。然而,看起来有了这个协议,我只能用Data/NSData来转换它,这使得上面的过程变成了这样:
- 同上
- 编写一个符合可编码协议的结构/类
- 使用NSJSONSerialization将info字典(即{quot;user_id":123456}(再次编码为Data/NSData
- 使用JSONDecoder将这些数据解码到我的模型中
所以我的问题是,可编码对象可以直接从字典派生吗?
编辑:
谢谢你们的回答,但让我澄清一下。尽管响应JSON格式有些固定,但有许多不同的请求和响应,因此info部分是不同的。例如:
- R1:{"code":"0","info":{"user_id":123456}}是对用户id请求的响应
- R2:{"代码":"0","信息":{"温度":20,"国家":"伦敦"}}是对天气温度请求的响应
因此,Codable类/结构应该只从info部分构建,而不是从整个响应数据构建,这就是为什么我不能简单地应用步骤2&4来实现这一点。
看起来您拥有的JSON如下所示:
let r1 = Data("""
{"code": "0", "info": {"user_id": 123456}}
""".utf8)
let r2 = Data("""
{"code": "0", "info": {"temperature": 20, "country": "London"}}
""".utf8)
并且";信息";看起来像这样的类型:
struct UserID: Codable {
var userId: Int
}
struct WeatherTemperature: Codable {
var temperature: Int
var country: String
}
我假设一个解码器可以进行snakeCase转换(你可以通过实现CodingKeys或其他什么来代替它(:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
鉴于此,您需要一个通用的响应类型:
struct Response<Info: Codable>: Codable {
var code: String
var info: Info
}
这样,您就可以直接从JSON对象解码响应:
let userID = try decoder.decode(Response<UserID>.self, from: r1).info
let weather = try decoder.decode(Response<WeatherTemperature>.self, from: r2).info
否,不是直接的,是的,有点像。困惑的我来解释。
Codable
用于对Data
进行编码/解码,JSON是目前最流行的编码方式,但您也可以使用plist或编写自己的自定义编码器/解码器。
但坚持使用标准编码器,如果您想从Dictionary
转换为符合Decodable
的类型,其中包括符合Codable
的任何内容,您可以将Dictionary
编码为JSON(或plist(,然后将生成的Data
解码为Decodable
。。。这基本上就是你描述的过程。请注意,如果Dictionary
的键和值类型都是Codable
(例如[String: Int]
(,则可以使用JSONEncoder/Decoder而不是JSONSerialization
。但是,如果是[String: Any]
,则需要使用JSONSerialization
,因为Any
不符合Codable
。
话虽如此,您可以将Decodable
扩展为包含一个接受Dictionary
的故障或抛出初始化器,在该初始化器中,您将字典编码为Data
,然后使用JSONDecoder
进行解码。这仍然是你已经在做的过程,除了Decodable
的任何东西都会自动提供,以便于使用。
extension Decodable
{
init<Key: Hashable, Value>(_ dict: [Key: Value]) throws where Key: Codable, Value: Codable
{
let data = try JSONEncoder().encode(dict)
self = try JSONDecoder().decode(Self.self, from: data)
}
}
但听起来你实际上并不需要任何可编码的东西从Dictionary
初始化,只是你的模型。在这种情况下,与其扩展Decodable
,不如扩展您的模型。
如果您的字典中有Any
或其他非Codable
值,则可以使用JSONSerialization
执行类似的操作:
extension Decodable
{
init<Key: Hashable>(_ dict: [Key: Any]) throws
{
let data = try JSONSerialization.data(withJSONObject: dict, options: [])
self = try JSONDecoder().decode(Self.self, from: data)
}
}
使用它:
struct MyModel: Codable {...}
let dict: [String: Any] = getDictionarySomehow()
let myModel = try MyModel(dict)
使用json解码器将json字符串转换为数据,然后转换为可编码结构
let json = "{"CustomerTypeID":3,"CustomerID":4330,"CustomerName":"Black :)","CustomerTypeName":"Member","IsWalkedInCustomer":false,"Email":"black@yopmail.com","Mobile":"+447400369852","AllowPartPaymentOnCore":true,"ServiceBookingList":[{"SaleStatusTypeID":2,"SaleStatusTypeName":"Paid","ServiceName":"Nail Polish Service","BookingStatusTypeID":1,"BookingStatusTypeName":"Booked","SaleID":71861,"ID":83756,"ServiceCategoryID":85,"ServiceID":173,"ServicePackageID":245,"Price":0.00,"TotalPrice":0.00,"CleaningTimeInMinute":10,"StartDate":"2021-06-30T00:00:00Z","StartTime":"11:00:00","EndTime":"11:30:00","AssignedToStaffID":268,"FacilityID":0,"Description":"","IsPartialPaid":false,"TotalAmountPaid":0.0,"SaleRefundAmount":0.00,"AssignedToStaffName":"Iqra Rasheed","CustomerMembershipID":null,"Duration":"00:30","LastUpdatedByName":"Iqra Rasheed","FacilityName":"","StaffImagePath":"Staff_fabf1c3a-e2bf-45c6-b7f1-3cddd17fb358.jpg","TotalTaxPercentage":22.00,"TotalDiscountAmount":0.00,"IsFree":false,"HasUnSubmittedForm":true,"HasAtleastOneMandatoryForm":true}]}"
let data = json.data(using: .utf8)
let decoder = JSONDecoder()
if let data = data, let model = try? decoder.decode(YourCodableModel.self, from: data) {
print(model)
}
{
"code": "0",
"info": {
"user_id": 123456
}
}
如上所述,只要结构匹配,就可以一步完成。
例如:
struct ServerResponse: Codable {
var code: Int
var info: [String:Int]
}
或者:
struct ServerResponse: Codable {
var code: Int
var info: Info
}
struct Info: Codable {
var user_id: Int
}
然后简单地
let serverResponse = try? JSONDecoder().decode(ServerResponse.self, from: your_server_json_data)