如何使用自定义类型属性从 JSON 初始化模型类

Posted

技术标签:

【中文标题】如何使用自定义类型属性从 JSON 初始化模型类【英文标题】:How to initialize model class from JSON with custom type attribute 【发布时间】:2017-08-04 12:02:21 【问题描述】:

我知道我可以为我的模型类创建可失败的初始化程序,以通过以下方式从 JSON 数据进行初始化:

struct Person 
    let firstName: String
    let middleName: String?

    init?(JSONData data:[String:AnyObject]) 
        guard let firstName = data["firstName"] as? String else  return nil 
        self.firstName = firstName
        self.middleName = data["middleName"] as? String
    

但是如果我在Person 中有另一个属性,它是另一个模型类类型呢?例如:

struct Person 
   let firstName: String
   let car: Car

   init?(JSONData data:[String: AnyObject]) 
      guard let firstName = data["firstName"] as! String,
            let car = data["car"] as! Car // this line doesn't work I guess
       else return nil
      self.firstName = firstName
      self.car = car
   


汽车是这样的:

struct Car 
    let year: Int
    let brand: String

使上述可失败初始化程序与自定义类型 Car 一起用于 JSON 数据解析的正确方法是什么?

例如JSON:

“firstName”: “John”,
 “car”: 
        “year”: 2009,
        “brand”: “BMW”
       

【问题讨论】:

【参考方案1】:

首先,您在guard 语句中没有返回可选参数。在其中将! 更改为?

其次,您应该将数据传递给初始化程序并检查它是否为 nil。

guard let firstName = data["firstName"] as? String,
            let carData = data["car"] as? [String: AnyObject],
            let car = Car(JSONData: carData)
       else return nil

【讨论】:

为什么在guard我不能用as!却必须用as? 这是因为guard 检查给定变量是否是可选的。 as! 表示变量将是一个隐式展开的可选,所以guard 提出的那种“跳过问题”。【参考方案2】:

使用此代码更改 Car 结构-

struct Car 
    let year: Int
    let brand: String

   init?(JSONData data:[String: AnyObject]) 
      guard let brand = data["brand"] as! String,
            let year = data["year"] as! Int 
       else return nil
      self.brand = brand
      self.year = year
   

并将 Person 结构更改为 this-

struct Person 
   let firstName: String
   let car: Car

   init?(JSONData data:[String: AnyObject]) 
      guard let firstName = data["firstName"] as! String,
            let car = Car(JSONData:data["car"])
       else return nil
      self.firstName = firstName
      self.car = car
   


【讨论】:

【参考方案3】:

你可以试试

struct Person 
    let firstName: String
    let car: Car

    init?(JSONData data:[String: AnyObject]) 
        guard let firstName = data["firstName"] as? String,
            let carJSON = data["car"] as? [String: AnyObject?],
            let car = Car(data: carJSON)
            else return nil
        self.firstName = firstName
        self.car = car
    


汽车

struct Car 
    let year: Int
    let brand: String

    init?(data: [String: AnyObject?]) 
        guard let brand = data["brand"] as? String,
            let year = data["year"] as? Int
            else return nil
        self.brand = brand
        self.year = year
    

【讨论】:

guardPerson 中,我应该使用as? Car 还是as! Car? & 为什么不是另一个? @Leem.fin guard 必须在条件中使用可选类型,因此应使用as? 而不是as!【参考方案4】:

随着 Swift 4+ 引入 Codable 用于使用外部表示(JSON,Plist)进行编码和解码

JsonData -

    let jsonExample = """

    "firstName": "John",
      "car": 
            "year": 2009,
            "brand": "BMW"
      


""".data(using: .utf8)!

采用 Codable 协议的结构-

 struct UserData: Codable
        var firstName: String
        var car: CarData
    
    struct CarData: Codable
        var year: Int
        var brand: String
        init(from decoder: Decoder) throws 
            let values = try decoder.container(keyedBy: CodingKeys.self)
            year = try values.decode(Int.self, forKey: .year)
            brand = try values.decode(String.self, forKey: .brand)
        
    

用法-

  let jsonDecoder = JSONDecoder()
        do 
            let modelResult = try jsonDecoder.decode(UserData.self,from: jsonExample)
            print("firstName is \(modelResult.firstName)")//prints John
            print("car brand is \(modelResult.car.brand)")//Prints BMW

         catch 
            print(error)
        

如果 Json 的结构与您的结构不同,则使用 CodingKeys

例子-

let jsonExample = """

    "firstNameOfBuyer": "John",
      "car": 
            "year-of-made": 2009,
            "brand-name": "BMW"
      


""".data(using: .utf8)!
    struct UserData: Codable 
        var firstName: String
        var car: CarData
        private enum CodingKeys: String, CodingKey 
            case firstName = "firstNameOfBuyer"
            case car
        

    
    struct CarData: Codable
        var year: Int
        var brand: String
        private enum CodingKeys: String, CodingKey 
            case year = "year-of-made"
            case brand = "brand-name"

        
        init(from decoder: Decoder) throws 
            let values = try decoder.container(keyedBy: CodingKeys.self)
            year = try values.decode(Int.self, forKey: .year)
            brand = try values.decode(String.self, forKey: .brand)
        
    

【讨论】:

以上是关于如何使用自定义类型属性从 JSON 初始化模型类的主要内容,如果未能解决你的问题,请参考以下文章

在 Django 中使用继承过滤模型

mutable.PriorityQueue 为具有更多属性的类自定义排序

如何将 JSON 解码为不同的类型?

如何以编程方式添加尺寸类自定义

CakePHP 类自定义导入

EF Core 5 - 如何将 EF.Functions.Like 与映射到 JSON 字符串的自定义属性一起使用?