sarthak taneja
sarthak taneja

Reputation: 59

Parsing JSON data using Codable in Swift

I have been using codable in Swift from a long time and it has almost solved all the issues I had until I encountered the below mentioned issue. Suppose, the data coming from the API is something like this:

 { 
     "name": "ABC",
     "contactNo": "123", 
     "gender": "M", 
     "dob": "02-01-1998", 
     "qualitifcation": "XYZ"
   }

Now, I want to parse some part of this data in One model and some part in another model and have the reference of another model in the first model. For Eg:

  struct ModelOne: Codable {
    let name: String?
    let contactNo: String?
    let modelTwoRef: ModelTwo?
    }
    
    struct ModelTwo: Codable {
    let gender: String?
    let dob: String?
    let qualification: String?
    }

I am not able to solve this problem. It would be really helpful is someone can help with the same.

Upvotes: 2

Views: 663

Answers (4)

Ahmet Bostancikli
Ahmet Bostancikli

Reputation: 33

struct ModelOne: Codable {
    var name: String
    var contactNo: String
    var gender: String
    var dob: Date
    var qualification: String
}

let json = """
{
    "name": "ABC",
    "contactNo": "123",
    "gender": "M",
    "dob": "02.01.1998",
    "qualification": "XYZ"
}
""".data(using: .utf8)!

let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yy"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
let response = try decoder.decode(ModelOne.self, from: json)

print("Name: \(response.name)")
print("Contact no: \(response.contactNo)")
print("Gender: \(response.gender)")
print("Date: \(response.dob)")
print("qualification \(response.qualification)")

Upvotes: 0

Larme
Larme

Reputation: 26006

You can solve this by overriding init(from:):

struct ModelOne: Codable {
    let name: String?
    let contactNo: String?
    let modelTwoRef: ModelTwo?

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.contactNo = try container.decode(String.self, forKey: .contactNo)
        self.modelTwoRef = try ModelTwo(from: decoder)
    }
}

struct ModelTwo: Codable {
    let gender: String?
    let dob: String?
    let qualification: String?
}

Test sample:

let jsonStr = """
    {
    "name": "ABC",
    "contactNo": "123",
    "gender": "M",
    "dob": "02-01-1998",
    "qualification": "XYZ"
    }
    """

let jsonData = Data(jsonStr.utf8)

do {
    let model = try JSONDecoder().decode(ModelOne.self, from: jsonData)
    print(model)
} catch {
    print("Error: \(error)")
}

Small diff from your question which I guess was a typo: "qualification“ vs "qualitifcation".

Upvotes: 2

mgratzer
mgratzer

Reputation: 630

You need to implement decoding by hand in ModelOne and pass on the decoder. You can do this by implementing the init method of ModelOne like this:

struct ModelOne: Codable {
    let name: String?
    let contactNo: String?
    let modelTwoRef: ModelTwo?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decodeIfPresent(String.self, forKey: .name)
        contactNo = try container.decodeIfPresent(String.self, forKey: .contactNo)
        modelTwoRef = try ModelTwo(from: decoder)
    }
    
    enum CodingKeys: String, CodingKey {
        case name, contactNo
    }
}

struct ModelTwo: Codable {
    let gender: String?
    let dob: String?
    let qualification: String?
}

I hope it solves your problem. /Martin

Upvotes: 0

Piyush Ghoghari
Piyush Ghoghari

Reputation: 11

Create JSON decode function like below function

func decode<T: Decodable>(from data: Data, to decodableType: T.Type) -> T? {
    do {
        let decodedData = try decoder.decode(decodableType, from: data)
        return decodedData
    } catch let error {
        print(error)
    }
    return nil
}

check API response data nil or not using guard statement

guard let decoadedData = self.jsonHelper.decode(from: data, to: ModelOne.self) else {
                return
            }

Upvotes: 1

Related Questions