How to initialize model class from JSON with custom type attribute

时间:2017-08-04 12:02:22

标签: ios json swift3

I know I can create failable initializer for my model class to initialize from JSON data by :

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

But what if I have another attribute in Person which is another model class type? For example:

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
   }

}

Car looks like this:

struct Car {
    let year: Int
    let brand: String
}

What is the proper way to make the failable initializer above work with custom type Car for JSON data parsing?

e.g. JSON:

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

4 个答案:

答案 0 :(得分:0)

Firstly, you are not returning optionals in your guard statement. Change ! to ? in them.

Secondly, you should pass the data to the initializer and check if it is nil.

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

答案 1 :(得分:0)

you can try like

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
    }

}

and in 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
    }
}

答案 2 :(得分:0)

Change Car struct with this code-

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

And change Person struct to 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 :(得分:0)

由于Swift 4+引入了Codable For Encoding&使用外部表示解码(JSON,Plist)

JsonData -

    let jsonExample = """
{
    "firstName": "John",
      "car": {
            "year": 2009,
            "brand": "BMW"
      }

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

使用可编码协议的结构 -

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