Decoding structure use by SwiftyJson and Alamofire - swift

How can I decode data to a structure using Alamofire and SwiftyJSON? My attempts give me errors like that
"No value associated with key CodingKeys(stringValue: \"user\",
intValue: nil)
Here is my code, my try doesn't give me the result when I use non-optional values, they respond to me with NIL values
Alamofire.request(url, method: .post, parameters: params, encoding: URLEncoding.default, headers: nil).responseJSON { (response) in
if response.data != nil {
switch response.result {
case.failure( let error):
print(error)
case.success(let val):
var json = JSON(val)
print(json)
guard let data = response.data else {return}
do {
let root = try JSONDecoder().decode(MainInfo.self, from: data)
print(root.submodel)
}
catch {
print("Bigerror")
print(error)
}
This is my structure
struct user: Codable {
var push_id:String?
var name:String?
var id:String?
var role_id:String?
var taxi_park_id:Int?
var car_number:String?
enum CodingKeys:String,CodingKey {
case push_id = "push_id"
case name = "name"
case id = "id"
case role_id = "role_id"
case taxi_park_id = "taxi_park_id"
case car_number = "car_number"
}
}
struct MainInfo : Decodable {
let model: String?
let submodel: String?
let user:user
enum CodingKeys:String,CodingKey {
case model = "model"
case submodel = "submodel"
case user = "user"
}
}
This is my pretty printed json
{
"facilities" : [
],
"model" : "AMC",
"taxi_park" : "Taxi +",
"submodel" : "Gremlin",
"user" : {
"role_id" : 2,
"push_id" : "dW7Cy-ItcDo:APA91bH62zJJKKz0t9VxP29H0iE2xhnQH0hDvKpGaHc5pknuTuZq2lMaj-EapQlN3O4dJF0ysSuCNOeb-2SdJaJaLIZcwHD3CCpeNpz6UVeGktoCm2ykL2rNXF5-ofQckvz1xTvVO0V6",
"taxi_park_id" : 0,
"id" : 3,
"name" : "China",
"car_number" : "X123OOO"
}
}

First of all your question has nothing to do with SwiftyJSON as you are using Codable.
Second of all name the structs with starting capital letter (User), that avoids confusion like let user : user
The error is misleading. All .._id values except push_id are Int rather than String. It's very easy to distinguish strings from all other types: Strings are always wrapped in double quotes.
And if you pass the convertFromSnakeCase key decoding strategy you don't need CodingKeys at all
struct MainInfo : Decodable {
let model : String
let submodel : String
let user : User
}
struct User: Decodable {
let pushId : String
let name : String
let id : Int
let roleId : Int
let taxiParkId : Int
let carNumber : String
}
...
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let root = try decoder.decode(MainInfo.self, from: data)
print(root.submodel)
} catch { print(error) }

Try this code, also a simple tip, we use coding keys in swift because sometimes we have to receive an inconvenient parameter keys but we also want to use it simple and clearly in the struct therefore CodingKeys are helpful in your case you using CodingKeys to decode the same parameter name i added the following taxiPark propriety to give you a hint on why they are useful, for example: i want to parse a JSON that have a key called
Person_ID_From_School
with coding keys i can do that with a better naming simple as personId and so on
struct MainInfo : Decodable {
let model: String?
let submodel: String?
let user:user
let taxiPark: String?
let facilities: [String?]?
enum CodingKeys:String,CodingKey {
case model = "model"
case submodel = "submodel"
case user = "user"
case taxiPark = "taxi_park"
case facilities = "facilities"
}
}

Related

Facing problem with JSON parsing in swift

My REST returns following Array, and only one item.
{
"Table1": [
{
"Id": 1,
"ClauseNo": "2-111",
"Title": "Testing Title",
"URL": "http://www.google.com",
}
]
}
I'm trying to use the Codable as following:
struct Clause: Codable {
var Id: Int
var ClauseNo: String
var Title: String
var URL: String
}
What I'm doing wrong with following code?
func parse(json: Data) -> Clause {
var clause: Clause?
if let jsonClause = try? JSONDecoder().decode([Clause].self, from: json) {
clause = jsonClause
}
return clause!
}
As I mentioned above, I only have 1 item not more than that.
This is very common mistake, you are ignoring the root object
struct Root : Decodable {
private enum CodingKeys : String, CodingKey { case table1 = "Table1" }
let table1 : [Clause]
}
struct Clause: Decodable {
private enum CodingKeys : String, CodingKey { case id = "Id", clauseNo = "ClauseNo", title = "Title", url = "URL" }
let id: Int
let clauseNo: String
let title: String
let url: URL
}
...
func parse(json: Data) -> Clause? {
do {
let result = try JSONDecoder().decode(Root.self, from: json)
return result.table1.first
} catch { print(error) }
return nil
}
Side note: Your code crashes reliably if an error occurs
I tend to handle these scenarios like this:
struct Table1 : Codable {
var clauses: [Clause]
struct Clause: Codable {
let Id: Int // variable names should start with a lowercase
let ClauseNo: String // letter :)
let Title: String
let URL: String
}
}
And then when you're decoding you end up with a table from which you want the first element, something like:
if let jsonTable = try? JSONDecoder().decode(Table1.self, from: json) {
clause = jsonTable.clauses[0]
}

Decode Json Data using JsonDecoder and Alamofire

I am trying decode Json data into my Model.
This is my model
struct Devices : Codable {
var id :String?
var description :String?
var status : Int?
}
var heroes = Devices()
print(DeviceId)
let loginParam: [String: Any] = [
"id": DeviceId
]
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 5
manager.request("http://13.13.13.004/website/api/Customer/DeviceControl", method: .post , parameters: loginParam, encoding: JSONEncoding.prettyPrinted)
.responseData { response in
let json = response.data
do{
let decoder = JSONDecoder()
//using the array to put values
heroes = try decoder.decode(Devices.self, from: json!)
}catch let err{
print(err)
}
this code doesn't get in catch block.
But heroes values return nill.
When ı try use NsDictionary
Its give result.
This is a common mistake: You forget the root object
struct Root : Decodable {
private enum CodingKeys: String, CodingKey { case resultCount, devices = "results" }
let resultCount : Int
let devices : [Device]
}
And name the device struct in singular form (devices is an array of Device instances) and declare the members as non-optional
struct Device : Decodable {
var id : String
var description : String
var status : Int
}
...
var heroes = [Device]()
...
let result = try decoder.decode(Root.self, from: json!)
heroes = result.devices

Convert an Int JSON value to String enum case

Here's an simplified version of the class:
class Movie: Codable {
var name: String
var genre: MovieGenre
init(name: String, genre: MovieGenre) {
self.name = name
self.genre = genre
}
}
enum MovieGenre: String, Codable {
case action
case drama
case horror
}
And the JSON:
{
"name" : "Test",
"genre" : 1
}
I know the relation between the JSON genre value and the MovieGenre enum is:
1 = action
2 = drama
3 = horror
Using JSONDecoder, how can I convert the JSON genre Int value to my enum MovieGenre?
I would like not to have to write an init from decoder, because it would be very verbose having to convert each attribute manually.
Here's an example:
let movie = Movie(name: "Test", genre: .action)
let jsonEncoder = JSONEncoder()
let jsonDecoder = JSONDecoder()
do {
// encoding
let jsonData = try jsonEncoder.encode(movie)
let jsonString = String(data: jsonData, encoding: .utf8)
print("JSON String : " + jsonString!) // prints: JSON String : {"name":"Test","genre":"action"}
// decoding
let json = "{\"name\":\"Test\",\"genre\":1}".data(using: .utf8)!
_ = try jsonDecoder.decode(Movie.self, from: json)
} catch {
print(error) // prints: typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "genre", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))
}
Your type for your enum does not match with the type in the JSON.
I changed the Type for your enum to Int and set the initial value for action (as the default would be 0) and got the expected result without custom decoding.
Trying this in a playground:
import UIKit
import PlaygroundSupport
let jsonData = """
{
"name" : "Test",
"genre" : 1
}
""".data(using: .utf8)!
class Movie: Codable {
var name: String
var genre: MovieGenre
init(name: String, genre: MovieGenre) {
self.name = name
self.genre = genre
}
}
enum MovieGenre: Int, Codable {
case action = 1
case drama
case horror
}
let decoder = JSONDecoder()
let result = try? decoder.decode(Movie.self, from: jsonData)
print(result?.name)
print(result?.genre)
print(result?.genre.rawValue)
Output is:
Optional("Test")
Optional(__lldb_expr_39.MovieGenre.action)
Optional(1)
This should also encode in the same way.

Parse complex json code

I have the following JSON code and want to parse it in Swift. I use Alamofire to get the JSON and have created a struct for the parsing:
{
"-8802586561990153106-1804221538-5":{
"zug":{
"klasse":"RB",
"nummer":"28721"
},
"ankunft":{
"zeitGeplant":"1804221603",
"zeitAktuell":"1804221603",
"routeGeplant":[
"Wiesbaden Hbf",
"Mainz Hbf"
]
},
"abfahrt":{
"zeitGeplant":"1804221604",
"zeitAktuell":"1804221604",
"routeGeplant":[
"Gro\u00df Gerau",
"Klein Gerau",
"Weiterstadt"
]
}
},
"8464567322535526441-1804221546-15":{
"zug":{
"klasse":"RB",
"nummer":"28724"
},
"ankunft":{
"zeitGeplant":"1804221657",
"zeitAktuell":"1804221708",
"routeGeplant":[
"Aschaffenburg Hbf",
"Mainaschaff"
]
},
"abfahrt":{
"zeitGeplant":"1804221658",
"zeitAktuell":"1804221709",
"routeGeplant":[
"Mainz-Bischofsheim"
]
}
}
}
I have created a struct for this that looks like this:
struct CallResponse: Codable {
struct DirectionTrain: Codable {
struct Train: Codable {
let trainClass: String
let trainNumber: String
}
struct Arrival: Codable {
let line: String
let eta: Date
let ata: Date
let platform: String
let route: [String]
}
struct Departure: Codable {
let line: String
let etd: Date
let atd: Date
let platform: String
let route: [String]
}
}
}
The rest of my code is:
Alamofire.request(url!).responseJSON { response in
switch response.result {
case .success:
let decoder = JSONDecoder()
let parsedResult = try! decoder.decode(CallResponse.self, from: response.data!)
case .failure(let error):
print(error)
}
}
When I run this code the error message is:
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "train", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"train\", intValue: nil) (\"train\").", underlyingError: nil))
Can anyone help me find my problem? Thank you for your answers!
The problem is merely that your structs look nothing at all like your JSON!
Your JSON is a dictionary whose keys have names like "-8802586561990153106-1804221538-5" and "8464567322535526441-1804221546-15". But I don't see you declaring any struct that deals with those keys.
Then each of those turns out to be a dictionary with keys like "zug", "ankunft", and "abfahrt". But I don't see you declaring any struct that deals with those keys either.
And then the "zug" has keys "klasse" and "nummer"; you don't have those either.
And so on.
Either your structs must look exactly like your JSON, or else you must define CodingKeys and possibly also implement init(from:) to deal with any differences between your structs and your JSON. I suspect that the keys "-8802586561990153106-1804221538-5" and "8464567322535526441-1804221546-15" are unpredictable, so you will probably have to write init(from:) in order to deal with them.
For example, I was able to decode your JSON like this (I do not really recommend using try!, but we decoded without error and it's just a test):
struct Entry : Codable {
let zug : Zug
let ankunft : AnkunftAbfahrt
let abfahrt : AnkunftAbfahrt
}
struct Zug : Codable {
let klasse : String
let nummer : String
}
struct AnkunftAbfahrt : Codable {
let zeitGeplant : String
let zeitAktuell : String
let routeGeplant : [String]
}
struct Top : Decodable {
var entries = [String:Entry]()
init(from decoder: Decoder) throws {
struct CK : CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
return nil
}
}
let con = try! decoder.container(keyedBy: CK.self)
for key in con.allKeys {
self.entries[key.stringValue] =
try! con.decode(Entry.self, forKey: key)
}
}
}
// d is a Data containing your JSON
let result = try! JSONDecoder().decode(Top.self, from: d)

Expected to decode Array<Any> but found a dictionary instead

I was fetching data from an API returning an array but needed to replace it by an API that has "sub levels":
RAW:
ETH:
USD:
TYPE: "5"
MARKET: "CCCAGG"
FROMSYMBOL: "ETH"
TOSYMBOL: "USD"
PRICE: 680.89
CHANGEPCT24HOUR : -9.313816893529749
Here is my struct:
struct Ethereum: Codable {
let percentChange24h: String
let priceUSD: String
private enum CodingKeys: String, CodingKey {
case priceUSD = "PRICE", percentChange24h = "CHANGEPCT24HOUR"
}
}
And the implementation:
func fetchEthereumInfo(completion: #escaping (Ethereum?, Error?) -> Void) {
let url = URL(string: "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
if let ethereumUSD = try JSONDecoder().decode([Ethereum].self, from: data).first {
print(ethereumUSD)
completion(ethereumUSD, nil)
}
} catch {
print(error)
}
}
task.resume()
}
The console prints typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
I can't really figure out what to update in my code or what this form of API is
First of all the JSON does not contain any array. It's very very easy to read JSON. There are only 2 (two!) collection types, array [] and dictionary {}. As you can see there are no square brackets at all in the JSON string.
Any (sub)dictionary {} has to be decoded to its own type, so it's supposed to be
struct Root : Decodable {
private enum CodingKeys : String, CodingKey { case raw = "RAW" }
let raw : RAW
}
struct RAW : Decodable {
private enum CodingKeys : String, CodingKey { case eth = "ETH" }
let eth : ETH
}
struct ETH : Decodable {
private enum CodingKeys : String, CodingKey { case usd = "USD" }
let usd : USD
}
struct USD : Decodable {
private enum CodingKeys : String, CodingKey {
case type = "TYPE"
case market = "MARKET"
case price = "PRICE"
case percentChange24h = "CHANGEPCT24HOUR"
}
let type : String
let market : String
let price : Double
let percentChange24h : Double
}
To decode the JSON and and print percentChange24h you have to write
let result = try JSONDecoder().decode(Root.self, from: data)
print("percentChange24h", result.raw.eth.usd.percentChange24h)