Please help make this work I've been trying to figure out JSON and Swift for a week and facing this problem for 5 hours so far today.
Error Received
ERROR WHEN DECODING JSON keyNotFound(CodingKeys(stringValue: "MP", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "MP", intValue: nil) ("MP").", underlyingError: nil))
YEAR IS NIL
Code
struct UserDay: Codable {
let MP: UserMP
let WP: UserWP
}
struct UserMP: Codable {
let M: [UserM]
let S: [UserS]
}
struct UserM : Codable {
let title: String
let description: String
let time: String
}
struct UserS : Codable {
let title: String
let description: String
let time: String
}
struct UserWP: Codable {
let WP: [WPData]
}
struct WPData: Codable {
let title: String
let values: [Int]
}
class LogDataHandler {
public func grabJSONInfo(){
guard let jsonURL = Bundle(for: type(of: self)).path(forResource: "newLogData", ofType: "json") else { return }
guard let jsonString = try? String(contentsOf: URL(fileURLWithPath: jsonURL), encoding: String.Encoding.utf8) else { return }
// print(jsonString)
// Print Info for TESTING
var year: UserDay?
do {
year = try JSONDecoder().decode(UserDay.self, from: Data(jsonString.utf8))
} catch {
print("ERROR WHEN DECODING JSON \(error)")
}
guard let results = year else {
print("YEAR IS NIL")
return
}
print(results)
}
}
JSON
{
"01/01/2020": {
"MP" : {
"M" : [
{"title" : "m1", "description" : "1", "time" : "12:30pm"},
{"title" : "m2", "description" : "2", "time" : "1:30pm"},
{"title" : "m3", "description" : "3", "time" : "2:30pm"}
],
"S" : [
{"title" : "s1", "description" : "1", "time" : "1pm"}
]
},
"WP" : [
{ "title" : "abc", "values" : [12, 10, 6]},
{ "title" : "def", "values" : [8]}
]
},
"01/29/2020": {
"MP" : {
"M" : [{"title" : "m1", "description" : "1", "time" : "12:30pm"}],
"S" : [{"title" : "s1", "description" : "1", "time" : "12:30pm"}]
},
"WP" :[{ "title" : "def", "values" : [8]}]
}
}
First, replace let WP: UserWP with an array:
struct UserDay: Codable {
let MP: UserMP
let WP: [WPData]
}
Then decode [String:UserDay] instead of UserDay:
try JSONDecoder().decode([String:UserDay].self, from: Data(jsonString.utf8))
Note that this will return a dict with key-value pairs "01/01/2020":UserDay object.
This means you can't assign a dictionary [String: UserDay] to a UserDay variable. Instead you can access the dict by a key which is some date (eg. "01/01/2020") and then assign it to your UserDay variable:
let result = try JSONDecoder().decode([String:UserDay].self, from: Data(jsonString.utf8))
let someYear = result["01/01/2020"]
Note that someYear will be optional so you may want to provide a default value or force-unwrap it.
Related
I'm suffering for a week, you are my last hope, here is the JSON that I'm trying to parse, it comes from the server
[
1,
null,
[
{
“categories” : [
{
“id” : 26,
“category” : “sweaters”
}
],
“sellingPrice” : “6000.00",
“statusSite” : “a”,
“type” : “wear”,
“id” : 30024,
“payoutType” : “3”,
“style” : “Avant Garde”,
“count” : 1,
“status” : “active”,
“images” : [
{
“productId” : 30024,
“image” : “2000000211176-066e7e39b0a590501fe85155a571258e.jpg”,
“imageMin” : “2000000211176-a7e0626b3512f517f23b1e091224e759.jpg”,
“name” : “6.jpg”
},
{
“productId” : 30024,
“image” : “2000000211176-25bc8175742d959448f0c8211d7753f6.jpg”,
“imageMin” : “2000000211176-6212cd8a58a4e7269d282651eb316429.jpg”,
“name” : “3.jpg”
},
{
“productId” : 30024,
“image” : “2000000211176-3bd4648416c5a1e525417673a0328060.jpg”,
“imageMin” : “2000000211176-e3bf05a38f430013e9077637244e45b8.jpg”,
“name” : “1.jpg”
},
{
“productId” : 30024,
“image” : “2000000211176-561ce49fbb4de29b8ef905f66279f432.jpg”,
“imageMin” : “2000000211176-67f41b0a32e48a380d5b5d27c7dcaea8.jpg”,
“name” : “4.jpg”
},
{
“productId” : 30024,
“image” : “2000000211176-c37ff0bd838d2f8eaabaa09e45a8b07f.jpg”,
“imageMin” : “2000000211176-b6e96c3ff88965f301ade7257eb8653e.jpg”,
“name” : “2.jpg”
},
{
“productId” : 30024,
“image” : “2000000211176-f3c14b80583e079d11b9fc5556af5f8b.jpg”,
“imageMin” : “2000000211176-4907e8502f739b56579f53a5eaa8d0a9.jpg”,
“name” : “5.jpg”
}
],
“priceInAct” : null,
“additionallyHeldAmounts” : null,
“vendorCode” : “000000238”,
“amountOfCommission” : null,
“searchWords” : “10sei0otto, ,dieciseizerotto,sweater ,Свитер ,Gently used,Отличное,Black,Черный,,“,
“id1c” : “2000000211176",
“brand” : {
“name” : “10sei0otto”,
“id” : 987
},
“saleStatus” : “A”,
“tradeIn” : “N”,
“composition” : null,
“isHidden” : false,
“created” : “2022-12-11T00:00:00.000Z”,
“collections” : [
],
“name” : “Black Leather Patched Sweater”,
“userId” : 2312,
“sex” : “M”,
“imagesPath” : “[\“2000000211176_1.jpg\“,\“2000000211176_2.jpg\“,\“2000000211176_3.jpg\“,\“2000000211176_4.jpg\“,\“2000000211176_5.jpg\“,\“2000000211176_6.jpg\“]”,
“badgeSale” : 0,
“badge” : “{\“background\“:\“#FF00FF\“,\“image\“:\“https:\/\/toppng.com\/uploads\/preview\/sale-badge-sale-tag-symbol-11563037290rmf7mns4rx.png\“,\“text\“:\“\”}“,
“features” : [
],
“timestampPrice” : null,
“condition” : “Gently used”,
“place” : 1,
“saleDate” : null,
“languages” : [
{
“description” : “”,
“discount” : “0.00”,
“languageCode” : “en-EN”,
“size” : “XL”,
“price” : “170.00”
},
{
“description” : “”,
“discount” : “0.00”,
“languageCode” : “ru-RU”,
“size” : “XL”,
“price” : “11000.00”
}
],
“userId1c” : “000000238”
}
]
]
import Alamofire
import SwiftyJSON
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var jsonResultsTextView: UITextView!
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var buttonLbl: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
buttonLbl.setTitle("PUSH", for: .normal)
}
#IBAction func buttonAction(_ sender: Any) {
AF.request("https://beta.server.spin4spin.com/auth/login", method: .post, parameters: login, encoding: JSONEncoding.default).response { response in
print(response)
}
}
#IBAction func get(_ sender: Any) {
if let urlString = URL(string: "https://beta.server.spin4spin.com/admin/raw-catalog?search=2000000211176") {
URLSession.shared.dataTask(with: urlString) { data, response, error in
if let data = data {
print(data)
do {
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)
print(responseJSON as Any)
let decoder = JSONDecoder()
let json = try? decoder.decode([[Dats]].self, from: data)
print(json as Any)
} catch let error {
print(error)
}
}
}.resume()
}
}
}
struct Dats: Codable
{
let integer: Int
let null: String?
struct ResponseClass: Codable
{
struct FinalClassArray: Codable
{
let sellingPrice, statusSite, type: String
let id: Int
let payoutType, style: String
let count: Int
let status: String
let vendorCode: String
let searchWords, id1C: String
let saleStatus, tradeIn: String
let isHidden: Bool
let created: String
let name: String
let userID: Int
let sex, imagesPath: String
let badgeSale: Int
let badge: String
let condition: String
let place: Int
let userId1C: String
}
}
}
It throws a Nil error when trying to decode the values
enter image description here
Tried using Alamofire, SwiftyJson nothing helps to avoid errors
The main problem is that the root object is a heterogenous array, which cannot be decoded synthesized. This is very unusual and cumbersome.
You have to implement init(from decoder: and decode each item in the array manually. I'm talking about Decodable, SwiftyJSON has become obsolete.
And there are three properties which don't match the keys, id1c, userId and userId1c (note the different lowercase and uppercase characters)
These structs match the given JSON
struct Dats: Decodable
{
let integer: Int
let null: String?
let items : [FinalClassArray]
init(from decoder: Decoder) throws {
var arrayContainer = try decoder.unkeyedContainer()
self.integer = try arrayContainer.decode(Int.self)
self.null = try arrayContainer.decodeIfPresent(String.self)
self.items = try arrayContainer.decode([FinalClassArray].self)
}
}
struct FinalClassArray: Decodable
{
let sellingPrice, statusSite, type: String
let id: Int
let payoutType, style: String
let count: Int
let status: String
let vendorCode: String
let searchWords, id1c: String
let saleStatus, tradeIn: String
let isHidden: Bool
let created: String
let name: String
let userId: Int
let sex, imagesPath: String
let badgeSale: Int
let badge: String
let condition: String
let place: Int
let userId1c: String
}
And this is the code to decode the data (I renamed the function get as loadData)
#IBAction func loadData(_ sender: Any) {
guard let url = URL(string: "https://beta.server.spin4spin.com/admin/raw-catalog?search=2000000211176") else { return }
URLSession.shared.dataTask(with: url) { data, _, error in
if let error { print(error); return }
do {
let json = try JSONDecoder().decode(Dats.self, from: data!)
print(json)
} catch {
print(error)
}
}.resume()
}
This is the json output from https://maps.googleapis.com/maps/api/distancematrix/json?origins=rijssen&destinations=almelo&mode=driving&language=en&key=
{ "destination_addresses" : [ "Almelo, Netherlands" ],
"origin_addresses" : [ "Rijssen, Netherlands" ],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "14.1 km",
"value" : 14090
},
"duration" : {
"text" : "21 mins",
"value" : 1267
},
"status" : "OK"
}
]
}
],
"status" : "OK"
}
this is the struct in Swift
struct Model: Codable{
let destination_addresses : [String]
let origin_addresses : [String]
let rows : [Elements]
let status : String
}
struct Elements: Codable {
let elements:[Distance]
}
struct Distance: Codable{
let distance:Value
let duration:Value
let status:String
}
struct Value: Codable{
let text:String
let value:Int
}
This is the api request
guard let url = URL(string: api) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
if let data = data {
let result = try JSONDecoder().decode([Model].self, from: data)
error in compiler
The data couldn’t be read because it isn’t in the correct format.
Why is the code not working?
Thank you i.a.
i want to Parsing a json but Xcode Show this message : keyNotFound(CodingKeys(stringValue: "Id", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"Id\", intValue: nil) (\"Id\").", underlyingError: nil))
JSON :
{
"result": [
{
"Id": 5,
"Title": "Test1",
"EnTitle": "Story and Novel"
},
{
"Id": 38,
"Title": "Test2",
"EnTitle": " Motivational"
}
],
"status": {
"message": "Confirm",
"success": true,
"systemDateTime": "2019-11-01T12:07:05+03:30",
"httpStatusCode": 200
}
}
Model :
struct Result : Decodable {
let Id : Int
let Title : String
let EnTitle : String
}
class ResultInitialiser {
let Id : Int
let Title : String
let EnTitle : String
init(Id:Int, Title:String, EnTitle: String) {
self.Id = Id
self.Title = Title
self.EnTitle = EnTitle
}
}
View Controller :
var genresFetch = [Result]()
var structGenresFetch = [ResultInitialiser]()
let headers : HTTPHeaders = ["Token" : "6f8652e3-d9d9-4b34-9455-0fa32e82ec58"]
AF.request(BASE_URL!, method: .get, headers: headers).response { (response) in
do {
self.genresFetch = [try JSONDecoder().decode(Result.self, from: response.data!)]
for eachProduct in self.genresFetch {
let recived_Data = ResultInitialiser(Id: eachProduct.Id, Title: eachProduct.Title, EnTitle: eachProduct.EnTitle)
self.structGenresFetch.append(recived_Data)
print(eachProduct.Title)
DispatchQueue.main.async {
self.tvMainApi.reloadData()
}
}
}catch {
print(error)
}
}
JSONDecoder will try to decode the JSON from the root. At the root level, there are only two keys, results and status. The Decodable type that you have passed to it, Result, has neither of those keys.
You need to create another struct:
struct Response {
let results: [Result]
}
And decode this struct instead:
do {
// notice the change to this line
self.genresFetch = try JSONDecoder().decode(Response.self, from: response.data!).results
...
}catch {
print(error)
}
IMO, you don't need the extra class called ResultInitialiser. What's wrong with just using the struct as your model?
"No value associated with key CodingKeys(stringValue: \"Id\", intValue: nil)
The root of the json contains result key only and doesn't have id or the other keys you submit with your Result struct , so You need
self.genresFetch = try JSONDecoder().decode(Root.self, from: response.data!)
print(self.genresFetch.result)
struct Root : Decodable {
let result : [Result]
}
struct Result: Decodable {
let id: Int
let title, enTitle: String
enum CodingKeys: String, CodingKey {
case id = "Id"
case title = "Title"
case enTitle = "EnTitle"
}
}
I am a beginner programmer and I couldn't parse my json file. I can see datas in my console but I need help to parse json datas. I shared my json datas from url and I would like to parse with Alamofire. I installed PodFile with Alamofite and SwiftyJSON. I don't have any error but I need help for parse it.
Also I created string Arrays for following datas. I will append data in arrays.
[
{
"id" : 1,
"team" : "Liverpool",
"players" : [
{
"id" : 2,
"name" : "Alisson",
"position" : "Goal Keeper",
"number" : "13"
},
{
"id" : 3,
"name" : "Salah",
"position" : "Forward",
"number" : "10"
}
],
"trophies" : [
"2019 champions league",
"2005 champions league"
],
"logoUrl" : "url"
},
{
"id" : 4,
"team" : "Real Madrid",
"players" : [
{
"id" : 5,
"name" : "Ramos",
"position" : "Defender",
"number" : "4"
},
{
"id" : 6,
"name" : "Benzema",
"position" : "Forward",
"number" : "9"
}
],
"trophies" : [
"2018 champions league",
"2017 champions league",
"2016 champions league"
],
"logoUrl" : "url"
}
]
import Alamofire
import SwiftyJSON
func fetchJsonData(){
DispatchQueue.main.async {
Alamofire.request(url).responseData { response in
guard let data = response.data else { return }
do {
let res = try JSONDecoder().decode([PageData].self, from:data)
print(res)
} catch {
print("having trouble converting it to a dictionary" , error)
}
}
}
}
// this is my modal file
struct PageData: Codable {
let team: String
let players: [Player]
let trophies: [String]
let logoUrlL: String
}
struct Player: Codable {
let id: Int
let name,position, number: String?
}
You need responseData for using JSONDecoder
Alamofire.request(url).responseData { response in
guard let data = response.data else { return }
do {
let res = try JSONDecoder().decode([PageData].self, from:data)
print(res)
} catch {
print("having trouble converting it to a dictionary" , error)
}
}
Also players and trophies are arrays
struct PageData: Codable {
let team: String
let players: [Player]
let trophies: [String]
let logoUrlL: String
}
struct Player: Codable {
let id: Int
let name,position, number, type, quantity: String?
}
When I hit my configuration API with Postman I am given the following json response back. In this response the two apiVersion keys are numbers and not strings.
{
"data": {
"availability": {
"auth": true,
"ab": true,
"cd": true
},
"helloWorldConfiguration": {
"apiKey": "abcefg",
"rootUrl": "https://foo",
"apiVersion": 3
},
"fooBarConfiguration": {
"baseUrl": "https://foo",
"apiVersion": 1,
"privateApiPath": "",
"publicApiPath": "dev",
"tokenPath": ""
}
},
"errors": []
}
When I try to decode it it fails with a typeMismatch error. When I output the contents of the response, I see the following which looks fine to me.
data = {
availability = {
auth = 1;
ab = 1;
cd = 1;
};
helloWorldConfiguration = {
apiVersion = 1;
baseUrl = "https://foo";
privateApiPath = "";
publicApiPath = dev;
tokenPath = "";
};
fooBarConfiguration = {
apiKey = abcefg;
apiVersion = 3;
rootUrl = "https://foo";
};
};
errors = (
);
The error given to me indicates that data.helloWorldConfiguration.apiVersion is of type string instead of int. We can see from the original HTTP response I get from Postman that's not the case.
typeMismatch(Swift.Int, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), CodingKeys(stringValue: "helloWorldConfiguration", intValue: nil), CodingKeys(stringValue: "apiVersion", intValue: nil)], debugDescription: "Expected to decode Int but found a string/data instead.", underlyingError: nil))
21:17:40 ERROR Unable to decode the response data into a model representation.
My model represents those properties as integers so it would appear that it receives the response and considers those numbers to be strings, which they're not.
public struct ServerConfiguration: Decodable {
let availability: AvailabilityConfiguration
let helloWorldConfiguration: HelloWorldConfiguration
let fooBarConfiguration: FooBarConfiguration
init(availability: AvailabilityConfiguration, helloWorldConfiguration: HelloWorldConfiguration, fooBarConfiguration: FloatSinkConfiguration) {
self.availability = availability
self.helloWorldConfiguration = helloWorldConfiguration
self.fooBarConfiguration = fooBarConfiguration
}
}
public struct FooBarConfiguration: Decodable {
let baseUrl: String
let apiVersion: Int
let privateApiPath: String
let publicApiPath: String
let tokenPath: String
init(baseUrl: String, apiVersion: Int, privateApiPath: String, publicApiPath: String, tokenPath: String) {
self.baseUrl = baseUrl
self.apiVersion = apiVersion
self.privateApiPath = privateApiPath
self.publicApiPath = publicApiPath
self.tokenPath = tokenPath
}
}
public struct AvailabilityConfiguration: Decodable {
let auth: Bool
let ab: Bool
let cd: Bool
init(auth: Bool, ab: Bool, cd: Bool) {
self.auth = auth
self.ab = ab
self.cd = cd
}
}
public struct HelloWorldConfiguration: Codable {
let apiKey: String
let rootUrl: String
let apiVersion: Int
init(apiKey: String, rootUrl: String, apiVersion: Int) {
self.apiKey = apiKey
self.rootUrl = rootUrl
self.apiVersion = apiVersion
}
}
As you can see my apiVersion members are both of type integer along with the json response. What am I doing wrong here? I assume what's happening is Swift is considering the numbers in the json string, regardless of how they're actually represented in the json. Is that the case?
Edit to show utf8 string of Alamofire response data
21:44:06 INFO GET: https:foo/configuration
{
"data" : {
"availability" : {
"auth" : true,
"ab" : true,
"cb" : true
},
"helloWorldConfiguration" : {
"apiKey" : "abcd",
"rootUrl" : "https://foo",
"apiVersion" : "3"
},
"fooBarConfiguration" : {
"baseUrl" : "https://foo",
"apiVersion" : "1",
"privateApiPath" : "",
"publicApiPath" : "dev",
"tokenPath" : "auth/token"
}
},
"errors" : []
}
It would seem that despite the API correctly returning apiVersion as a number, Swift is turning it into a string. Am I decoding it incorrectly?
func getRoute<TResponseData: Decodable>(route:String, completion: #escaping (TResponseData) -> Void) throws {
let headers = try! self.getHeaders(contentType: ContentType.json)
let completeUrl: String = self.getUrl(route: route, requestUrl: nil)
logger.info("GET: \(completeUrl)")
Alamofire.request(
completeUrl,
method: .get,
parameters: nil,
encoding: JSONEncoding.default,
headers: headers)
.validate()
.responseJSON { (response) -> Void in
self.logger.info("GET Response: \(String(describing:response.response?.statusCode))")
switch response.result {
case .success(_):
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(Date.toFooBarDate)
do {
let result = try decoder.decode(TResponseData.self, from: response.data!)
completion(result)
} catch DecodingError.dataCorrupted(let error) {
self.logger.error(error.underlyingError!)
return
} catch {
print(response.result.value!)
print(error)
self.logger.error("Unable to decode the response data into a model representation.")
return
}
}
}
I checked in my playground and it seems that everything is working fine.To find the real issue i think you are required to provide the real url from where you are getting json and can be checked with alamofire
import Foundation
let json = """
{
"data": {
"availability": {
"auth": true,
"ab": true,
"cd": true
},
"helloWorldConfiguration": {
"apiKey": "abcefg",
"rootUrl": "https://foo",
"apiVersion": 3
},
"fooBarConfiguration": {
"baseUrl": "https://foo",
"apiVersion": 1,
"privateApiPath": "",
"publicApiPath": "dev",
"tokenPath": ""
}
},
"errors": []
}
"""
let data = json.data(using: .utf8)
struct Response : Codable {
let data : Data?
let errors : [String]?
}
struct Availability : Codable {
let auth : Bool?
let ab : Bool?
let cd : Bool?
}
struct Data : Codable {
let availability : Availability?
let helloWorldConfiguration : HelloWorldConfiguration?
let fooBarConfiguration : FooBarConfiguration?
}
struct FooBarConfiguration : Codable {
let baseUrl : String?
let apiVersion : Int?
let privateApiPath : String?
let publicApiPath : String?
let tokenPath : String?
}
struct HelloWorldConfiguration : Codable {
let apiKey : String?
let rootUrl : String?
let apiVersion : Int?
}
let decoder = JSONDecoder()
let response = try decoder.decode(Response.self, from: data!)
print(response)
And here is the response
Response(data: Optional(__lldb_expr_11.Data(availability: Optional(__lldb_expr_11.Availability(auth: Optional(true), ab: Optional(true), cd: Optional(true))), helloWorldConfiguration: Optional(__lldb_expr_11.HelloWorldConfiguration(apiKey: Optional("abcefg"), rootUrl: Optional("https://foo"), apiVersion: Optional(3))), fooBarConfiguration: Optional(__lldb_expr_11.FooBarConfiguration(baseUrl: Optional("https://foo"), apiVersion: Optional(1), privateApiPath: Optional(""), publicApiPath: Optional("dev"), tokenPath: Optional(""))))), errors: Optional([]))