swift4 encoding decoding for model class of nested json parsing - json

I have a model class of swift which was created based on a nested json response, it follows like below
struct RootClass : Codable {
let details : String?
let itemCount : Int?
let list : [List]?
enum CodingKeys: String, CodingKey {
case details = "Details"
case itemCount = "ItemCount"
case list = "List"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
details = try values.decodeIfPresent(String.self, forKey: .details)
itemCount = try values.decodeIfPresent(Int.self, forKey: .itemCount)
list = try values.decodeIfPresent([List].self, forKey: .list)
}
}
struct List : Codable {
let companyID : Int?
let employeeCount : Int?
let employeeUser : EmployeeUser?
enum CodingKeys: String, CodingKey {
case companyID = "CompanyID"
case employeeCount = "EmployeeCount"
case employeeUser = "EmployeeUser"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
companyID = try values.decodeIfPresent(Int.self, forKey: .companyID)
employeeCount = try values.decodeIfPresent(Int.self, forKey: .employeeCount)
employeeUser = try EmployeeUser(from: decoder)
}
}
struct EmployeeUser : Codable {
let mobileNumber : String?
let name : String?
enum CodingKeys: String, CodingKey {
case mobileNumber = "MobileNumber"
case name = "Name"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
mobileNumber = try values.decodeIfPresent(String.self, forKey: .mobileNumber)
name = try values.decodeIfPresent(String.self, forKey: .name)
}
}
and my json response is
{
"Details": null,
"List": [
{
"CompanyID": 140,
"EmployeeUser": {
"Name": " raghu2",
"MobileNumber": "8718718710"
},
"EmployeeCount": 0
},
{
"CompanyID": 140,
"EmployeeUser": {
"Name": "new emp reg",
"MobileNumber": "1"
},
"EmployeeCount": 0
}
],
"ItemCount": 0
}
I am trying to parse it like
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(RootClass.self, from: data)
print(gitData.itemCount ?? "")
print(gitData.list![0].employeeUser?.mobileNumber ?? "")
}
catch let err {
print("Err", err)
}
I am able to get the values of root class and list but I am getting nil values under employee user section.

Your code a few problems:
All your keys are optional. The vendor API will tell you what keys are always present and which one are optional. Follow that.
decodeIfPresent will silently fail if it cannot decode a key. When debugging your app, you want things to fail with a bang so you can fix the error before going to production.
You wrote way more code than needed. All those init(from decoder: ) functions are not needed. One one did cause your problem.
Your problem was caused by this line:
struct List : Codable {
init(from decoder: Decoder) throws {
...
employeeUser = try EmployeeUser(from: decoder)
}
}
You are asking Swift to decode to same JSON to a List and a EmployeeUser object. Obviously, that's not valid. But when you decode list inside RootClass, you call decodeIfPresent:
// In Rootclass
list = try values.decodeIfPresent([List].self, forKey: .list)
This call silently failed and you never knew what the problem was!
Solution
Change how you initialize employeeUser to this:
employeeUser = try values.decodeIfPresent(EmployeeUser.self, forKey: .employeeUser)
But the most elegant solution is to delete all those init(from decoder: ). The compiler will synthesize them for you.
And finally, fix those optionals!

Related

Swift - How to have multiple decoder method for a single model

struct Classroom: Codable {
let teacher: String
let id: Int
let status: Bool
init(from decoder: Decoder) throws {
...
...
}
}
Now I need a way to create Classroom instance with a simple String
{ "classroom": "Test" }//received from API
let classRoom = ClassRoom(teacher: "Test", id: 0, status: true)
Now I need to add a secondary decoder method which can create this classroom instance using the "classroom": "Test" data. The "Test" value should be used as value for "teacher" and other properties should contain default values.
I know I can decode the String value and create a new initializer. Is there a way to directly decode String to this model object?
if i understand well, i assume you have a bad json format like below
[
{
"teacher":"test",
"id":5,
"status":true
},
{
"classroom":"Test"
}
]
And you want to decode both objects, you can do the following
let data = """
[
{
"teacher": "test",
"id": 5,
"status": true
},
{
"classroom": "Test"
}
]
""".data(using: .utf8)!
struct Classroom: Codable {
let teacher: String
let id: Int
let status: Bool
private enum CodingKeys: CodingKey {
case teacher, id, status
}
private enum SecCodingKeys: CodingKey {
case classroom
}
init(from decoder: Decoder) throws {
let value = try decoder.container(keyedBy: CodingKeys.self)
let secValue = try decoder.container(keyedBy: SecCodingKeys.self)
let teacher_1 = try value.decodeIfPresent(String.self, forKey: .teacher)
let teacher_2 = try secValue.decodeIfPresent(String.self, forKey: .classroom)
teacher = teacher_1 ?? teacher_2 ?? ""
id = try value.decodeIfPresent(Int.self, forKey: .id) ?? 0
status = try value.decodeIfPresent(Bool.self, forKey: .status) ?? false
}
}
do {
let rooms = try JSONDecoder().decode([Classroom].self, from: data)
print(rooms.map(\.teacher))
} catch {
print(error)
}
and the result,
["test", "Test"]
Decode the second, nested case, as another type
struct SimpleClassroom: Decodable {
let classroom: String
}
and then have a computed property for mapping to the original type with default values
extension SimpleClassroom {
var classroomValue: Classroom {
Classroom(teacher: classroom, id: 0, status: false)
}
}
If "Test" is a valid description of classroom, and you want to go ahead and create the classroom, then you have a number of options.
If you know classrooms from a given API endpoint will always be in this string format, you can use the decoder's context dictionary to tell it up front which strategy to use to decode the classroom. If sometimes a classroom is a properly formed dictionary, and sometimes it's just a string, and you want to proceed either way, then you have to handle that case in the init(from:).
Either way you're looking at a custom init method. The second case, where you handle both types, would look like this:
init(from decoder: Decoder) throws {
// Do we have a single-value container?
do {
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
self.teacher = string
self.id = 0
self.status = true
return
} catch {
// OK, it was a dictionary
}
let container = try decoder.container(keyedBy: CodingKeys.self)
self.teacher = try container.decode(String.self, forKey: .teacher)
self.id = try container.decode(Int.self, forKey: .id)
self.status = try container.decode(Bool.self, forKey: .status)
}
Given this made-up, horrible JSON:
[
"Test",
{ "teacher": "Mr Chips", "id": 0, "status": true }
]
let rooms = try JSONDecoder().decode([Classroom].self, from: data)
Gives you two valid Classroom types in an array.

Parsing Dynamic JSON Model in Swift [duplicate]

This question already has answers here:
Swift JSOn Decoding Error: Expected to decode Array<Any> but found a dictionary instead
(2 answers)
Closed 1 year ago.
My app must parse JSON data. The software (IceStats) that generates the data, though, generates JSON with two slightly different structures. Sometimes, the "source" value is an array of dictionaries and sometimes it just one dictionary. I can parse the JSON when it is one way or the other, but I don't know how to handle it both ways.
Here is the JSON in the Array version:
{
"icestats": {
"admin": "dontcontactme#localhost",
"host": "server.badradio.biz",
"location": "Airport",
"server_id": "Icecast 2.4.4",
"server_start": "Mon, 26 Apr 2021 12:50:47 -0500",
"server_start_iso8601": "2021-04-26T12:50:47-0500",
"source": [
{
"audio_info": "bitrate=128",
"bitrate": 128,
"genre": "Automation",
"listener_peak": 7,
"listeners": 0,
"listenurl": "http://server.badradio.biz:8000/ambient",
"server_description": "No show is running, tune in for selections from the venerable tape series \"Comfortable & Economical\"",
"server_name": "Comfortable & Economical",
"server_type": "audio/mpeg",
"server_url": "badradio.biz",
"stream_start": "Fri, 30 Apr 2021 06:51:49 -0500",
"stream_start_iso8601": "2021-04-30T06:51:49-0500",
"title": "Vol-15-A",
"dummy": null
},
{
"listeners": 0,
"listenurl": "http://server.badradio.biz:8000/stream",
"dummy": null
}
]
}
}
And here it is in the Dictionary version:
{
"icestats": {
"admin": "dontcontactme#localhost",
"host": "server.badradio.biz",
"location": "Airport",
"server_id": "Icecast 2.4.4",
"server_start": "Mon, 26 Apr 2021 12:50:47 -0500",
"server_start_iso8601": "2021-04-26T12:50:47-0500",
"source": {
"audio_info": "bitrate=128",
"bitrate": 128,
"genre": "Automation",
"listener_peak": 2,
"listeners": 0,
"listenurl": "http://server.badradio.biz:8000/ambient",
"server_description": "No show is running, tune in for selections from the venerable tape series \"Comfortable & Economical\"",
"server_name": "Comfortable & Economical",
"server_type": "audio/mpeg",
"server_url": "badradio.biz",
"stream_start": "Wed, 28 Apr 2021 02:18:31 -0500",
"stream_start_iso8601": "2021-04-28T02:18:31-0500",
"title": "Vol-13-A",
"dummy": null
}
}
}
Finally, here is my data model that handles the Array version:
import Foundation
struct StreamData: Decodable {
let icestats: IceStats
}
struct IceStats: Decodable {
let source: [Source]
}
struct Source: Decodable {
let server_name: String?
let stream_start: String?
let title: String?
let server_description: String?
let server_url: String?
let genre: String?
}
Any help is greatly appreciated. Ideally, I could just change the format of the JSON, but I am not able to.
You can add some custom decoding logic to try both cases:
struct IceStats: Decodable {
var source: [Source]
enum CodingKeys : CodingKey {
case source
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let elements = try? container.decode([Source].self, forKey: .source) {
source = elements
} else if let element = try? container.decode(Source.self, forKey: .source) {
source = [element]
} else {
throw DecodingError.dataCorruptedError(forKey: .source, in: container, debugDescription: "Must be either a single or multiple sources!")
}
}
}
But this can get really long if you also want to decode other properties in IceStats, because you'll have to manually write the decoding code for those too. To avoid this, you can use a property wrapper:
#propertyWrapper
struct MultipleOrSingle<Element: Decodable>: Decodable {
let wrappedValue: [Element]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let elements = try? container.decode([Element].self) {
wrappedValue = elements
} else if let element = try? container.decode(Element.self) {
wrappedValue = [element]
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Container neither contains a single, nor multiple \(Element.self)!")
}
}
}
// Now you can just do this!
struct IceStats: Decodable {
#MultipleOrSingle
var source: [Source]
}
As per your array response your class looks like below and where you get response just decode that response class you will get all data
import Foundation
struct Response : Codable {
let icestats : Icestat?
enum CodingKeys: String, CodingKey {
case icestats = "icestats"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
icestats = Icestat(from: decoder)
}
}
struct Icestat : Codable {
let admin : String?
let host : String?
let location : String?
let serverId : String?
let serverStart : String?
let serverStartIso8601 : String?
let source : [Source]?
enum CodingKeys: String, CodingKey {
case admin = "admin"
case host = "host"
case location = "location"
case serverId = "server_id"
case serverStart = "server_start"
case serverStartIso8601 = "server_start_iso8601"
case source = "source"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
admin = try values.decodeIfPresent(String.self, forKey: .admin)
host = try values.decodeIfPresent(String.self, forKey: .host)
location = try values.decodeIfPresent(String.self, forKey: .location)
serverId = try values.decodeIfPresent(String.self, forKey: .serverId)
serverStart = try values.decodeIfPresent(String.self, forKey: .serverStart)
serverStartIso8601 = try values.decodeIfPresent(String.self, forKey: .serverStartIso8601)
source = try values.decodeIfPresent([Source].self, forKey: .source)
}
}
struct Source : Codable {
let audioInfo : String?
let bitrate : Int?
let dummy : AnyObject?
let genre : String?
let listenerPeak : Int?
let listeners : Int?
let listenurl : String?
let serverDescription : String?
let serverName : String?
let serverType : String?
let serverUrl : String?
let streamStart : String?
let streamStartIso8601 : String?
let title : String?
enum CodingKeys: String, CodingKey {
case audioInfo = "audio_info"
case bitrate = "bitrate"
case dummy = "dummy"
case genre = "genre"
case listenerPeak = "listener_peak"
case listeners = "listeners"
case listenurl = "listenurl"
case serverDescription = "server_description"
case serverName = "server_name"
case serverType = "server_type"
case serverUrl = "server_url"
case streamStart = "stream_start"
case streamStartIso8601 = "stream_start_iso8601"
case title = "title"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
audioInfo = try values.decodeIfPresent(String.self, forKey: .audioInfo)
bitrate = try values.decodeIfPresent(Int.self, forKey: .bitrate)
dummy = try values.decodeIfPresent(AnyObject.self, forKey: .dummy)
genre = try values.decodeIfPresent(String.self, forKey: .genre)
listenerPeak = try values.decodeIfPresent(Int.self, forKey: .listenerPeak)
listeners = try values.decodeIfPresent(Int.self, forKey: .listeners)
listenurl = try values.decodeIfPresent(String.self, forKey: .listenurl)
serverDescription = try values.decodeIfPresent(String.self, forKey: .serverDescription)
serverName = try values.decodeIfPresent(String.self, forKey: .serverName)
serverType = try values.decodeIfPresent(String.self, forKey: .serverType)
serverUrl = try values.decodeIfPresent(String.self, forKey: .serverUrl)
streamStart = try values.decodeIfPresent(String.self, forKey: .streamStart)
streamStartIso8601 = try values.decodeIfPresent(String.self, forKey: .streamStartIso8601)
title = try values.decodeIfPresent(String.self, forKey: .title)
}
}
decode it like
let jsonData = jsonString.data(using: .utf8)!
let response = try! JSONDecoder().decode(Response.self, from: jsonData)
print(response.icestats)

Swift decode JSON array of object based on property value type

How to decode an array of different JSON objects, where the same property on each object tells you what type to use to decode it:
let json =
"""
[
{
"#type": "FirstObject",
"number": 1
},
{
"#type": "SecondObject",
"name": "myName"
}
]
"""
Here is some code based on this similar answer which gets most of the way there, but fails because it doesn't know what CodingKeys are for .data:
struct FirstObject: MyData {
var dataType: String
var number: Int
enum CodingKeys: String, CodingKey {
case dataType = "#type"
case number
}
}
struct SecondObject: MyData {
var dataType: String
var name: String
enum CodingKeys: String, CodingKey {
case dataType = "#type"
case name
}
}
struct SchemaObj: Decodable
{
var dataType: String
var data: MyData
enum CodingKeys: String, CodingKey {
case data
case dataType = "#type"
}
enum ParseError: Error {
case UnknownSchemaType(Any)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
dataType = try container.decode(String.self, forKey: .dataType)
switch dataType {
case "FirstObject":
data = try container.decode(FirstObject.self, forKey: .data)
case "SecondObject":
data = try container.decode(SecondObject.self, forKey: .data)
default:
throw ParseError.UnknownSchemaType(dataType)
}
}
}
do {
let data = Data(json.utf8)
let result = try JSONDecoder().decode([SchemaObj].self, from: data)
print(result)
} catch {
print(error)
}
Printed error is
keyNotFound(CodingKeys(stringValue: "data", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"data\", intValue: nil) (\"data\").", underlyingError: nil))
Thank you
You don't need the data coding key. Just decode the data property from the same decoder, based on the value of the JSON field:
enum CodingKeys: String, CodingKey {
case dataType = "#type"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
dataType = try container.decode(String.self, forKey: .dataType)
switch dataType {
case "FirstObject":
data = try FirstObject(from: decoder)
case "SecondObject":
data = try SecondObject(from: decoder)
default:
throw ParseError.UnknownSchemaType(dataType)
}
}
If you plan to add more types to that list, then the if/else if can can become hard to manage, you can use a lookup table to address this:
static let typeMapping: [String: MyData.Type] = [ "FirstObject": FirstObject.self ,
"SecondObject": SecondObject.self]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let dataType = try container.decode(String.self, forKey: .dataType)
guard let classToDecode = Self.typeMapping[dataType] else {
throw ParseError.UnknownSchemaType(dataType)
}
self.dataType = dataType
self.data = try classToDecode.init(from: decoder)
}

ignore null object in array when parse with Codable swift

i'm parsing this API with swift Codable
"total": 7,
"searchResult": [
null,
{
"name": "joe"
"family": "adam"
},
null,
{
"name": "martin"
"family": "lavrix"
},
{
"name": "sarah"
"family": "mia"
},
null,
{
"name": "ali"
"family": "abraham"
}
]
with this PaginationModel:
class PaginationModel<T: Codable>: Codable {
var total: Int?
var data: T?
enum CodingKeys: String, CodingKey {
case total
case data = "searchResult"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.total = try container.decodeIfPresent(Int.self, forKey: .total)
self.data = try container.decodeIfPresent(T.self, forKey: .data)
}
}
and User Model:
struct User: Codable {
var name: String?
var family: String?
}
i call jsonDecoder like this to parse API json:
let responseObject = try JSONDecoder().decode(PaginationModel<[User?]>.self, from: json)
now my problem is null in searchResult Array. it parsed correctly and when i access to data in paginationModel i found null in array.
how can i ignore all null when parsing API, and result will be an array without any null
In the first place, I would advise to always consider PaginationModel to be composed from arrays. You don't have to pass [User] as the generic type, you can just pass User. Then the parser can use the knowledge that it parses arrays and handle null automatically:
class PaginationModel<T: Codable>: Codable {
var total: Int?
var data: [T]?
enum CodingKeys: String, CodingKey {
case total
case data = "searchResult"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.total = try container.decodeIfPresent(Int.self, forKey: .total)
self.data = (try container.decodeIfPresent([T?].self, forKey: .data))?.compactMap { $0 }
}
}
You might want to remove optionals here and use some default values instead:
class PaginationModel<T: Codable>: Codable {
var total: Int = 0
var data: [T] = []
enum CodingKeys: String, CodingKey {
case total
case data = "searchResult"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.total = (try container.decodeIfPresent(Int.self, forKey: .total)) ?? 0
self.data = ((try container.decodeIfPresent([T?].self, forKey: .data)) ?? []).compactMap { $0 }
}
}
Simple solution, filter data after decoding
let responseObject = try JSONDecoder().decode(PaginationModel<[User?]>.self, from: data)
responseObject.data = responseObject.data?.filter{$0 != nil}
You may add an array type check within decode :
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.total = try container.decodeIfPresent(Int.self, forKey: .total)
self.data = try container.decodeIfPresent(T.self, forKey: .data)
//add the following:
if let array = self.data as? Array<Any?> {
self.data = ( array.compactMap{$0} as? T)
}
}
Note, you can just define the decodable variable that may be null/nil as [Float?] (or whatever type), with the optional '?' inside the array brackets.

Decoding two different JSON responses in one model class using Codable

Based on the requirement I got two different kinds of response from api. That is
{
"shopname":"xxx",
"quantity":4,
"id":1,
"price":200.00,
}
another response
{
"storename":"xxx",
"qty":4,
"id":1,
"amount":200.00,
}
Here both json values are decoding in same model class. Kindly help me to resolve this concern.
is it is possible to set value in single variable like qty and quantity both are stored in same variable based on key param availability
Here's an approach that lets you have only one property in your code, instead of two Optionals:
Define a struct that contains all the properties you need, with the names that you'd like to use in your code. Then, define two CodingKey enums that map those properties to the two different JSON formats and implement a custom initializer:
let json1 = """
{
"shopname":"xxx",
"quantity":4,
"id":1,
"price":200.00,
}
""".data(using: .utf8)!
let json2 = """
{
"storename":"xxx",
"qty":4,
"id":1,
"amount":200.00,
}
""".data(using: .utf8)!
struct DecodingError: Error {}
struct Model: Decodable {
let storename: String
let quantity: Int
let id: Int
let price: Double
enum CodingKeys1: String, CodingKey {
case storename = "shopname"
case quantity
case id
case price
}
enum CodingKeys2: String, CodingKey {
case storename
case quantity = "qty"
case id
case price = "amount"
}
init(from decoder: Decoder) throws {
let container1 = try decoder.container(keyedBy: CodingKeys1.self)
let container2 = try decoder.container(keyedBy: CodingKeys2.self)
if let storename = try container1.decodeIfPresent(String.self, forKey: CodingKeys1.storename) {
self.storename = storename
self.quantity = try container1.decode(Int.self, forKey: CodingKeys1.quantity)
self.id = try container1.decode(Int.self, forKey: CodingKeys1.id)
self.price = try container1.decode(Double.self, forKey: CodingKeys1.price)
} else if let storename = try container2.decodeIfPresent(String.self, forKey: CodingKeys2.storename) {
self.storename = storename
self.quantity = try container2.decode(Int.self, forKey: CodingKeys2.quantity)
self.id = try container2.decode(Int.self, forKey: CodingKeys2.id)
self.price = try container2.decode(Double.self, forKey: CodingKeys2.price)
} else {
throw DecodingError()
}
}
}
do {
let j1 = try JSONDecoder().decode(Model.self, from: json1)
print(j1)
let j2 = try JSONDecoder().decode(Model.self, from: json2)
print(j2)
} catch {
print(error)
}
Handling different key names in single model
Below are two sample json(dictionaries) that have some common keys (one, two) and a few different keys (which serve the same purpose of error).
Sample json:
let error_json:[String: Any] = [
"error_code": 404, //different
"error_message": "file not found", //different
"one":1, //common
"two":2 //common
]
let failure_json:[String: Any] = [
"failure_code": 404, //different
"failure_message": "file not found", //different
"one":1, //common
"two":2 //common
]
CommonModel
struct CommonModel : Decodable {
var code: Int?
var message: String?
var one:Int //common
var two:Int? //common
private enum CodingKeys: String, CodingKey{ //common
case one, two
}
private enum Error_CodingKeys : String, CodingKey {
case code = "error_code", message = "error_message"
}
private enum Failure_CodingKeys : String, CodingKey {
case code = "failure_code", message = "failure_message"
}
init(from decoder: Decoder) throws {
let commonValues = try decoder.container(keyedBy: CodingKeys.self)
let errors = try decoder.container(keyedBy: Error_CodingKeys.self)
let failures = try decoder.container(keyedBy: Failure_CodingKeys.self)
///common
self.one = try commonValues.decodeIfPresent(Int.self, forKey: .one)!
self.two = try commonValues.decodeIfPresent(Int.self, forKey: .two)
/// different
if errors.allKeys.count > 0{
self.code = try errors.decodeIfPresent(Int.self, forKey: .code)
self.message = try errors.decodeIfPresent(String.self, forKey: .message)
}
if failures.allKeys.count > 0{
self.code = try failures.decodeIfPresent(Int.self, forKey: .code)
self.message = try failures.decodeIfPresent(String.self, forKey: .message)
}
}
}
Below extension will help you to convert your dictionary to data.
public extension Decodable {
init(from: Any) throws {
let data = try JSONSerialization.data(withJSONObject: from, options: .prettyPrinted)
let decoder = JSONDecoder()
self = try decoder.decode(Self.self, from: data)
}
}
Testing
public func Test_codeble(){
do {
let err_obj = try CommonModel(from: error_json)
print(err_obj)
let failed_obj = try CommonModel(from: failure_json)
print(failed_obj)
}catch let error {
print(error.localizedDescription)
}
}
Use like
struct modelClass : Codable {
let amount : Float?
let id : Int?
let price : Float?
let qty : Int?
let quantity : Int?
let shopname : String?
let storename : String?
enum CodingKeys: String, CodingKey {
case amount = "amount"
case id = "id"
case price = "price"
case qty = "qty"
case quantity = "quantity"
case shopname = "shopname"
case storename = "storename"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
amount = try values.decodeIfPresent(Float.self, forKey: .amount)
id = try values.decodeIfPresent(Int.self, forKey: .id)
price = try values.decodeIfPresent(Float.self, forKey: .price)
qty = try values.decodeIfPresent(Int.self, forKey: .qty)
quantity = try values.decodeIfPresent(Int.self, forKey: .quantity)
shopname = try values.decodeIfPresent(String.self, forKey: .shopname)
storename = try values.decodeIfPresent(String.self, forKey: .storename)
}
}