Parse Codable Classes and Avoid Repetition

Parse Codable classes and avoid repetition

You can make your GeneralResponse generic and tell it what type to use when parsing the response:

class GeneralResponse<T: Codable>: Codable {
let http_status: Int?
let success, has_error: Bool?
let error: [String]?
let response: T?
}

class RegistrationResponseDetails: Codable {
let token: String?
let verified: Bool?
let message: String?
}

Then you can give it the inner response class when you parse the json:

let generalResponse = try JSONDecoder().decode(GeneralResponse<RegistrationResponseDetails>.self, from: jsonData)
// generalResponse.response is of type RegistrationResponseDetails?

Swift Codable Design

The easiest solution is to implement Decodable explicitly and set the properties of the class objects to the references to the horse and instructor objects in the library. This would add the following to the Library class:

enum CodingKeys: String, CodingKey {
case name
case instructors
case horses
case courses
}

init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(Double.self, forKey: .name)
instructors = try values.decode(Double.self, forKey: .instructors)
horses = try values.decode(Double.self, forKey: .horses)
courses = try values.decode(Double.self, forKey: .courses)

for course in courses {
for hourse in horses {
if(course.horse.id == horse.id){
course.horse = horse
break
}
}
for instructor in insturctors{
if(course.instructor.id == instructor.id){
course.instructor = instructur
break
}
}
}
}

how to use inheritance in decodable model

Rather than inheritance and classes use generics and structs because Decodable doesn't support inheritance by default.

For example create a struct JSONParser

struct JSONParser<T : Decodable> {

struct ResponseData<U : Decodable> : Decodable {
let total, count : Int
let results : [U]
}

let code : Int
let status : String
let data : ResponseData<T>

init(data: Data) throws {
let decoder = JSONDecoder()
data = try decoder.decode(ResponseData.self, from: data)
}
}

And use it for the dictionary containing id and title

struct Item {
let id : Int
let title : String
}

do {
let jsonParser = try JSONParser<Item>(data: data)
let results = jsonParser.data.results
} catch { print(error) }

Decode dictionary with identical keys from json in Swift

Ok so you want to know if there is a way of detecting the duplicate key error with JSONDecoder.

Honestly I didn't know the answer so I investigated a bit.

1. Let's fix your JSON keeping the duplicate key inconsistency

Your input JSON has several errors besides the duplicate key.

  1. A JSON should being with { or ] and ending with the same symbol as well
  2. There is a missing " at the end of "#FFFFFF
  3. There is a missing , at the end of
  "veryGoodBoy": {
"color": "#FFFFFF
}

Let's apply these fixes and we finally get a JSON where the only error is the duplicate key

let data = """
{
"myDogs": {

"goodBoy": {
"color": "#000000"
},

"veryGoodBoy": {
"color": "#FFFFFF"
},

"goodBoy": {
"color": "#FF0000"
}
}
}
""".data(using: .utf8)!

2. The Codable struct

Now we can define a Codable type (I usually prefer a struct when decoding JSONs) to match the input JSON

struct Response: Decodable {

let myDogs: [String: Dog]

struct Dog: Decodable {
let color: String
}
}

3. Decoding

Finally let's try decoding the JSON

do {
let dict = try JSONDecoder().decode(Response.self, from: data)
print(dict)
} catch {
print(error)
}

This code runs fine without raising an error.
But (of course) the resulting value has a missing entry

Why did I say "of course"? Because Swift dictionaries cannot have duplicated keys.

Response(
myDogs: [
"goodBoy": Dog(color: "#000000"),
"veryGoodBoy": Dog(color: "#FFFFFF")
]
)

4. Considerations

So as far as I can see, the answer to your question is: NO, we cannot detect a duplicate key in the original JSON with JSONDecoder.

Mapping Generic API Response Using Codable or ObjectMapper

Your decoding method and APIResponse will look like this considering user, countries, locations are decodable,

struct APIResponse<T: Decodable>: Decodable {
var data: T?
var code: Int
var success: Bool
var http_response: Int
}

func decode<T: Decodable>(data: Data, ofType: T.Type) -> T? {
do {
let decoder = JSONDecoder()
let res = try decoder.decode(APIResponse<T>.self, from: data)
return res.data
} catch let parsingError {
print("Error", parsingError)
}
return nil
}

Usage

let data = Data() // From the network api 

//User
let user = decode(data, ofType: User.self)

// Countries
let countries = decode(data, ofType: [Country].self)

// Locations
let locations = decode(data, ofType: [Location].self)

How parse json recursively using codable with swift

You don't need two types here at all, just one will do:

struct Item: Codable {
let name : String? // not all entries in your example has it, so it's optional
let uri: String?
let launch: [Item]? // same here, all leaf items doesn't have it

enum CodingKeys: String, CodingKey {
case name = "DisplayName"
case uri = "URI"
case launch = "Launch"
}
}

Not able to parse data using Codable and Alamofire 3.0

Not completely sure if I understand you correctly but I think this is what you're looking for. Kinda... :)

struct JSONResponse: Decodable {

struct AucListDoc: Decodable {
// I renamed some properties to suit conventions
let displayName: String
let listId: Int
let fkComId: Int
}

enum CodingKeys: String, CodingKey {
// don't need any key mapping here since the differences
// between the json's key names and the structs' properties'
// names are handled via the 'JSONDecoder's 'keyDecodingStrategy'
// (see the bottom of the answer)
case serverTime
case error

case data
case response

case numFound
case docs
}

// I only added the properties you seem to care about here
let serverTime: String
let error: Bool
let numFound: Int
let docs: [AucListDoc]

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

// you can immediately get 'serverTime' and 'error'
// from the outermost container
serverTime = try container.decode(String.self, forKey: .serverTime)
error = try container.decode(Bool.self, forKey: .error)

// since 'numFound' and 'docs' are contained in the nested
// 'response' container which itself is contained in the nested
// 'data' container you first have to get those nested containers
let data = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .data)
let response = try data.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)

// now you can get 'numFound' and 'docs' from the nested
// 'response' container
numFound = try response.decode(Int.self, forKey: .numFound)
docs = try response.decode([AucListDoc].self, forKey: .docs)
}

}

You can decode the jsonData like this (do not forget the JSONDecoders keyDecodingStrategy):

let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase

do {
let response = try jsonDecoder.decode(JSONResponse.self, from: jsonData)
print(response)
} catch {
print(error)
}


Related Topics



Leave a reply



Submit