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”
}}
答案 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)
}
}