Why are certain variables turning up nil? - json

I'm trying to decode this json but certain variables are nil. Most of these seem to be okay it's just a few that are not working properly. I don't have much experience with swift so I'm kind of at a loss of what to try next.
mycode:
struct Attr : Decodable {
let page: String?
let perpage: String?
let totalpages: String?
let total: String?
}
struct Images : Decodable {
let text: String?
let size: String?
}
struct Artist : Decodable {
let name: String?
let mbid: String?
let url: String?
}
struct Streamable : Decodable {
let text: String?
let fulltrack: String?
}
struct Track : Decodable {
let name: String?
let duration: String?
let playcount: String?
let listeners: String?
let mbid: String?
let url: String?
let streamable: Streamable?
let artist: Artist?
let images: [Images]?
}
struct Tracks : Decodable {
let track:[Track]?
}
struct Container : Decodable {
let tracks: Tracks?
let attr: Attr?
}
json:
{
"tracks": {
"track": [
{
"name": "bad guy",
"duration": "0",
"playcount": "870682",
"listeners": "125811",
"mbid": "",
"url": "https://www.last.fm/music/Billie+Eilish/_/bad+guy",
"streamable": {
"#text": "0",
"fulltrack": "0"
},
"artist": {
"name": "Billie Eilish",
"mbid": "",
"url": "https://www.last.fm/music/Billie+Eilish"
},
"image": [
{
"#text": "https://lastfm-img2.akamaized.net/i/u/34s/88d7c302d28832b53bc9592ccb55306b.png",
"size": "small"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/64s/88d7c302d28832b53bc9592ccb55306b.png",
"size": "medium"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/174s/88d7c302d28832b53bc9592ccb55306b.png",
"size": "large"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/300x300/88d7c302d28832b53bc9592ccb55306b.png",
"size": "extralarge"
}
]
},
...
images should contain an array of Images instead of nil, the majority of the other variables seem to be okay though

This is because when dealing with decodable the keys used in your serialized data format have to match the property names; In your case, the Image type contains text and size properties but the json contains #text and size (text =/= #text); That's also applicable for the type name Images not image.
However, citing from Encoding and Decoding Custom Types:
If the keys used in your serialized data format don't match the
property names from your data type, provide alternative keys by
specifying String as the raw-value type for the CodingKeys
enumeration.
Add CodingKeys as:
struct Images : Decodable {
let text: String?
let size: String?
enum CodingKeys: String, CodingKey {
case text = "#text"
case size
}
}
Tip: it is useful to use try/catch when facing an error while decoding your data:
do {
let result = try JSONDecoder().decode(Images.self, from: data)
} catch {
print(error.localizedDescription)
}
At this point it would print the error which might helpful to understand the issue most of the time.

Add CodingKey enum to map fields with # in the name
struct Images : Decodable {
let text: String?
let size: String?
enum CodingKeys: String, CodingKey {
case text = "#text"
case size
}
}
struct Streamable : Decodable {
let text: String?
let fulltrack: String?
enum CodingKeys: String, CodingKey {
case text = "#text"
case fulltrack
}
}
You also have an error in your Trackstruct, change images to image (or use CodingKey mapping there to). For more on decoding json see Apple's doc

Related

How to decode in model when custom object's key missing from json

I have a Json in which there is the possibility that a few keys can be missing
Full JSON looks like this
{
"data": "some text",
"id": "3213",
"title": "title",
"description": "description",
"customObj1": [{
"id": "2423",
"count": 35
........
.......
}]
"customObj2": [{
"number": "2423",
"name": "john"
........
.......
}]
"customObj3": [{
"like": "2423",
"Other": 9
........
.......
}]
}
the custom object (1,2,3) may or may not be available in JSON, How can I write the model in swift using codable and struct?
here is how the model (dummy) look like
// MARK: - Response
struct Response: Codable {
let data, id, title, ResponseDescription: String
let customObj1: [CustomObj1]
let customObj2: [CustomObj2]
let customObj3: [CustomObj3]
enum CodingKeys: String, CodingKey {
case data, id, title
case ResponseDescription = "description"
case customObj1, customObj2, customObj3
}
}
// MARK: - CustomObj1
struct CustomObj1: Codable {
let id: String
let count: Int
}
// MARK: - CustomObj2
struct CustomObj2: Codable {
let number, name: String
}
// MARK: - CustomObj3
struct CustomObj3: Codable {
let like: String
let other: Int
enum CodingKeys: String, CodingKey {
case like
case other = "Other"
}
}
I have tried using decode init but need help
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if (values.contains(. customObj1)) {
// here I need help }
else {
self.category = nil
}
}
}

How to decode a nested JSON struct with Swift Decodable?

Here is my JSON
{
"myItems": {
"item1name": [{
"url": "google.com",
"isMobile": true,
"webIdString": "572392"
}, {
"url": "hulu.com",
"isMobile": false,
"webIdString": "sad1228"
}],
"item2name": [{
"url": "apple.com",
"isMobile": true,
"webIdString": "dsy38ds"
}, {
"url": "Facebook.com",
"isMobile": true,
"webIdString": "326dsigd1"
}, {
"url": "YouTube.com",
"isMobile": true,
"webIdString": "sd3dsg4k"
}]
}
}
The json can have multiple items (such as item1name, item2name, item3name,...) and each item can have multiple objects (the example has 2 objects for the first item and 3 objects for the second item).
Here is the structure I want it saved to (incomplete):
struct ServerResponse: Decodable {
let items: [MyItem]?
}
struct MyItem: Decodable {
let itemName: String?
let url: String?
let isMobile: Bool?
let webIdString: String?
}
In the example above, it would mean that the items list should have five MyItem objects. For example, these would be the MyItem objects:
#1:
itemName: item1name,
url: google.com,
isMobile: true,
webIdString: 572392
#2:
itemName: item1name,
url: hulu.com,
isMobile: false,
webIdString: sad1228
#3:
itemName: item2name,
url: apple.com,
isMobile: true,
webIdString: dsy38ds
#4:
itemName: item2name,
url: Facebook.com,
isMobile: true,
webIdString: 326dsigd1
#5:
itemName: item2name,
url: YouTube.com,
isMobile: true,
webIdString: sd3dsg4k
What would be the best way for me to do this using Decodable? Any help would be appreciated. I think this may be similar to this problem: How to decode a nested JSON struct with Swift Decodable protocol?
Use below code to decode nested JSON for your requirement:
import Foundation
// MARK: - Websites
struct Websites: Codable {
var myItems: MyItems
}
// MARK: - MyItems
struct MyItems: Codable {
var item1Name, item2Name: [ItemName]
enum CodingKeys: String, CodingKey {
case item1Name = "item1name"
case item2Name = "item2name"
}
}
// MARK: - ItemName
struct ItemName: Codable {
var url: String
var isMobile: Bool
var webIDString: String
enum CodingKeys: String, CodingKey {
case url, isMobile
case webIDString = "webIdString"
}
}
NOTE: change the name of the variables according to your need.
EDIT
If you're still facing issue, use this app to convert your JSON to Struct:
Quick Type
Here is the structure you are after:
struct Websites: Decodable {
let items: [Item]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
items = try container.decode([String: [Item]].self, forKey: .myItems).values.flatMap { $0 }
}
private enum Key: String, CodingKey {
case myItems
}
}
struct Item: Decodable {
let url: String
let isMobile: Bool
let webIdString: String
}

Issue parsing JSON not in array format

Essentially I would like to take the following JSON and use the data within my project.
To parse the data I need to create a structure - in the past I used this for arrays:
Structure:
struct ArrayStruct: Codable {
let info1: String
let info2: String
}
Array:
[
{
"info1": "Information",
"info2": "More"
}
]
What would the structure for the following data look like?
{
"error": false,
"response": "",
"message": "",
"general": {
"customer": "Customer1",
"code": "CO1234",
"name": "Machine Name",
"category": "machine"
},
"details": {
"taken": "July 15th 2020",
"created": "November 2nd 2020",
"packed": "",
"delivered": ""
}
}
I made a structure using struct with codeable for the data you gave
struct MainResponse : Codable {
let details : Detail?
let error : Bool?
let general : General?
let message : String?
let response : String?
// coding keys not required for this as the data keys are same but added just as an example
enum CodingKeys: String, CodingKey {
case details = "details"
case error = "error"
case general = "general"
case message = "message"
case response = "response"
}
}
struct Detail : Codable {
let created : String?
let delivered : String?
let packed : String?
let taken : String?
}
struct General : Codable {
let category : String?
let code : String?
let customer : String?
let name : String?
}
First You need to create your data Struct like this
struct MainResponse : Codable {
let error : Bool?
let response : String?
let message : String?
let general : General?
let details : Details?
}
Make your General Struct like this
struct General : Codable {
let customer : String?
let code : String?
let name : String?
let category : String?
}
Make your Detail Struct like this
struct Details : Codable {
let taken : String?
let created : String?
let packed : String?
let delivered : String?
}
its easy to create structures now days, there are various tools available, just input the response you will get the data structure, used the following to generate the structure
quicktype.io
// MARK: - Customer
struct Customer: Codable {
let error: Bool?
let response, message: String?
let general: General?
let details: Details?
}
// MARK: - Details
struct Details: Codable {
let taken, created, packed, delivered: String?
}
// MARK: - General
struct General: Codable {
let customer, code, name, category: String?
}

How to map this heterogeneous object with the model in swift?

Okay, so I am stuck at decoding the last item of this particular json by this the model, "payload" is always nil, Inside this "payload" object I can make my own json structure, I am able to decode the "text" but when it comes to the last item which is "payload", it is not working and is always nil.
I am not using any third-party library.
My Model Class.
struct DailougeFlowModel : Decodable {
// private enum CodingKeys : String, CodingKey {
// case responseId = "responseId"
// case queryResult = "queryResult"
// }
let responseId : String?
let queryResult : QueryResult?
}
struct QueryResult: Decodable {
// private enum CodingKeys : String, CodingKey {
// case fulfillmentText = "fulfillmentText"
// case fulfillmentMessages = "fulfillmentMessages"
// }
let fulfillmentText : String?
let fulfillmentMessages : [FulfillmentMessages]?
}
struct FulfillmentMessages: Decodable {
let text : TextModel?
let payLoad : Questions?
}
struct TextModel: Decodable {
let text : [String]?
}
struct Questions : Decodable{
let questions : [String]?
}
This json is what I am getting from the dailogeflow(V2). I am integrating a chatbot in the application.
{
"responseId": "2b879f78-cc05-4735-a7e8-067fdb53a81d-f6406966",
"queryResult": {
"fulfillmentMessages": [
{
"text": {
"text": [
"Text Here"
]
}
},
{
"text": {
"text": [
"Another Reply For Hi"
]
}
},
{
"payload": {
"questions": [
"Question One",
"Question Two",
"Question Three",
"Question Four"
]
}
}
]
}
}
Specify the inner model names as it is in the json response, if you want to specify your own model name then you would need to set an enum in each model just like the first model 'ResponseModel'
// MARK: - ResponseModel
struct ResponseModel: Codable {
let responseID: String
let queryResult: QueryResult
enum CodingKeys: String, CodingKey {
case responseID = "responseId"
case queryResult
}
}
// MARK: - QueryResult
struct QueryResult: Codable {
let fulfillmentMessages: [FulfillmentMessage]
}
// MARK: - FulfillmentMessage
struct FulfillmentMessage: Codable {
let text: Text?
let payload: Payload?
}
// MARK: - Payload
struct Payload: Codable {
let questions: [String]
}
// MARK: - Text
struct Text: Codable {
let text: [String]
}

Creating a list from Nested JSON using Decodable in Swift 4

I have been able to convert JSON to structs using Swift 4's Decodable, unfortunately i have been unable to do so with a JSON key called "list" , which is a list ([]) that contains other structs.
{
"cod":"200",
"message":0.0061,
"cnt":5,
"list":[
{
"dt":1522605600,
"main":{ },
"weather":[ ],
"clouds":{ },
"wind":{ },
"rain":{ },
"sys":{ },
"dt_txt":"2018-04-01 18:00:00"
},
{
"dt":1522616400,
"main":{ },
"weather":[ ],
"clouds":{ },
"wind":{ },
"rain":{ },
"sys":{ },
"dt_txt":"2018-04-01 21:00:00"
},
{
"dt":1522627200,
"main":{
"temp":277.21,
"temp_min":277.21,
"temp_max":277.506,
"pressure":1016.3,
"sea_level":1023.98,
"grnd_level":1016.3,
"humidity":84,
"temp_kf":-0.3
},
These are my structs, my approach was to make ForecastInstance the overall container which holds a property "list" (like the JSON) that is of type ForecastList (which holds the nested structs).
struct ForecastInstance : Decodable {
let list: [ForecastList]?
}
struct ForecastList : Decodable {
let dt : Int?
let weather : [Weather]?
let main : Main?
let wind : Wind?
}
struct Wind : Decodable {
let speed: Float
}
struct Coord : Decodable {
let lon : Float
let lat : Float
}
struct Main : Decodable{
let temp : Double
let pressure : Int
let humidity : Int
let temp_min: Double
let temp_max: Double
}
struct Weather : Decodable{
let id : Int
let main: String
let description: String
let icon: String
}
When i do the following in the view controller , it fails.
self.currentForecast = try
JSONDecoder().decode(ForecastInstance.self,from:data!)
Any help will be greatly appreciated.
This was my error
debugDescription: "Parsed JSON number <1017.42> does not fit in Int.", underlyingError: nil))
Changing the property pressure of type Int to float did the trick.
It works in Playground!
Here is a nice article about Codable:
Swift 4 Decodable: Beyond The Basics.
I hope it will be helpful.
let weatherJSON = """
{
"list": [{
"dt": 1522605600,
"main": {
"temp": 30,
"humidity": 50,
"pressure": 40
},
"weather": ["cloudly"],
"clouds": {},
"wind": {},
"rain": {},
"sys": {},
"dt_txt": "2018-04-01 18:00:00"
}]
}
""".data(using: .utf8)!
struct Main: Codable {
let temp: Int?
let humidity: Int?
let pressure: Int?
}
struct Weather: Codable {
let dt: Int?
let main: Main?
let weather: [String]?
}
struct WeatherList: Codable {
let list: [Weather]?
}
let weather = try JSONDecoder().decode(WeatherList.self, from: weatherJSON)
let count = "\(weather.list?.count ?? 0)"
print(count)
Your top level struct needs to look like this to mimic your JSON structure. The rest of your structs look fine if you are still getting errors I suggest you relook at those structs as well.
struct ForecastInstance : Decodable {
let cod: String?
let message: Int?
let cnt: Int?
let list: [ForecastList]?
}