簡體   English   中英

如何在 Swift 中直接將字典轉換為 Codable 實例?

[英]How to directly convert a Dictionary to a Codable instance in Swift?

我的應用程序從我們的服務器接收二進制數據。 在我的舊 Objective-C 代碼中,我按如下方式處理響應數據:

  1. 使用 NSJSONSerialization 將 NSData 轉換為 NSDictionary,其中包含如下數據:{"code": "0", "info": {"user_id": 123456}},
  2. 編寫一個 MTLModel 子類
  3. 使用 Mantle API 將上面的信息字典 {"user_id": 123456} 轉換成我的模型

現在我想用 Swift 來做這件事,我剛剛了解到 Codable 協議是多么方便。 但是,看起來這個協議實現了,我只能用Data/NSData來轉換它,這使得上面的過程變成了這樣:

  1. 和上面一樣
  2. 編寫一個符合 Codable 協議的結構體/類
  3. 使用 NSJSONSerialization 將信息字典 {"user_id": 123456} 再次編碼為 Data/NSData
  4. 使用 JSONDecoder 將此數據解碼為我的模型

所以我的問題是, Codable 對象可以直接從字典派生嗎?

編輯:

謝謝你們的回答,但讓我澄清一下。 雖然響應JSON格式有些固定,但是請求和響應有很多不同,所以信息部分是不同的。 例如:

  • R1: {"code": "0", "info": {"user_id": 123456}} 是用戶id請求的響應
  • R2: {"code": "0", "info": {"temperature": 20, "country": "London"}} 是天氣溫度請求的響應

因此, Codable類/結構應該僅從信息部分構建,而不是從整個響應數據構建,這就是為什么我不能簡單地應用第 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
}

我假設有一個解碼器可以進行蛇形大小寫轉換(您可以通過實現 CodingKeys 或其他方式來替換它):

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

鑒於此,您需要一個通用的 Response 類型:

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

將 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)
}

不,不是直接的,是的,有點。 使困惑? 我來解釋一下。

Codable旨在對Data進行編碼/解碼,JSON 是當今最流行的編碼,但您也可以使用 plist 或編寫自己的自定義編碼器/解碼器。

但是堅持使用標准編碼器,如果您想從Dictionary轉換為符合Codable的類型,您可以通過將Dictionary編碼為 JSON(或 plist)然后將生成的Data解碼為Codable東西...基本上是你描述的過程。 請注意,如果Dictionary的鍵和值類型都是Codable (例如[String: Int] ),您可以使用 JSONEncoder/Decoder 而不是JSONSerialization 但是,例如,如果是[String: Any] ,則您需要使用JSONSerialization ,因為Any不符合Codable

話雖如此,您可以擴展Codable以包含一個帶有Dictionary的可失敗或拋出初始值設定項,您可以在其中將Dictionary編碼為Data ,然后使用JSONDecoder進行解碼。 這仍然是您已經在做的過程,只是任何Codable都會自動使其易於使用。

extension Codable
{
    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初始化,而只是您的模型。 在這種情況下,與其擴展Codable ,不如擴展您的模型。

 {
  "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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM