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.
- A JSON should being with
{
or]
and ending with the same symbol as well - There is a missing
"
at the end of "#FFFFFF - 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 JSONDecoder
s keyDecodingStrategy
):
let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let response = try jsonDecoder.decode(JSONResponse.self, from: jsonData)
print(response)
} catch {
print(error)
}
Related Topics
How to Upload Images from The Browser to Amazon S3 Using Vapor and Leaf
Swift 4 JSONdecoder Optional Variable
Load a Spritekit Scene from Another Bundle
How to Change an Associated Value Within an Enum
Realm Write Transaction Failing, Despite Being in Transaction
How to Pass Data from Using Post/Form Leaf Template
Swift: Forward Keystrokes to a Different Process
Why Does My Swift Bundle Get The Wrong Principal Class
Popping Noise Between Audioqueuebuffers
Merging Two Arrays of Dictionaries Based on a Shared Value
Using Auto Layout to Orientate Stack Views Vertically in Portrait and Horizontally in Landscape
Non-Translucent UItabbar Creates Strange Grey Bar
Parse Codable Classes and Avoid Repetition
Switch Statement Where Value Is Int But Case Can Contain Array
Can't Override UItableviewdatasource and UItableviewdelegate
Swift - Connect Delegate to Custom Xib Cell