如何在Swift中直接将Dictionary转换为可编码实例



我的应用程序从我们的服务器接收二进制数据。在我的旧Objective-C代码中,我处理响应数据如下:

  1. 使用NSJSONSerialization将NSData转换为NSDictionary,其中包含以下数据:{quot;code":"0","info":{;user_id"123456}}
  2. 编写MTLModel子类
  3. 使用Mantle API将上述信息字典(即{"user_id":123456}(转换为我的模型

现在我想用Swift来做这件事,我刚刚学会了Codable协议有多方便。然而,看起来有了这个协议,我只能用Data/NSData来转换它,这使得上面的过程变成了这样:

  1. 同上
  2. 编写一个符合可编码协议的结构/类
  3. 使用NSJSONSerialization将info字典(即{quot;user_id":123456}(再次编码为Data/NSData
  4. 使用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)

相关内容

  • 没有找到相关文章

最新更新