Decode weird JSON to a normal struct? - json

To decode JSON, I had to create this struct:
struct Product: Decodable {
var title: String
var thumbnail: URL
var price: Price
var asin: String
}
struct Price: Decodable {
var current_price: Double
}
The decoder looks like this:
let product = try? JSONDecoder().decode(Product.self, from: data)
As you can see, JSON Price has nested property current_price, which is not very useful for me.
I have a few questions:
How can I get rid of Price and store it directly as double to Product?
Prop: thumbnail is a URL. I want to store it as a String. Is this possible?

You can try this
struct Product: Decodable {
var title: String
var thumbnail: String
var price: [String: Double]
var asin: String
var currentPrice: Double? { price["current_price"] }
}

Related

decoding and parsing nested json Swift

I want to parse this json file and decode and add them into list. However, couldn't find a way to that. I tried this:
struct flightsPost: Codable {
var data: flightDate
}
struct flightDate: Codable {
var origin: String
var destination: String
var price: Int
var airline: String
var flight_number: String
var departure_at: String
var return_at: String
var transfers: String
var expires_at: String
}
Couldn't find a place and how to put dates these structs. Please help I am really struggling.
Your structure is wrong. The data child is not a single object but a collection of type [String:flightDate] or if you decode it with a custom dateformatter [Date:flightDate].
struct flightsPost: Codable {
var data: [String:flightDate]
}

Swift 5 Parsing strange json format

I'm trying to parse JSON but keep getting incorrect format error. The JSON I get back from FoodData Central (the USDA's Nutrition API) is as follows:
{
dataType = "Survey (FNDDS)";
description = "Chicken thigh, NS as to cooking method, skin not eaten";
fdcId = 782172;
foodNutrients = (
{
amount = "24.09";
id = 9141826;
nutrient = {
id = 1003;
name = Protein;
number = 203;
rank = 600;
unitName = g;
};
type = FoodNutrient;
},
{
amount = "10.74";
id = "9141827";
nutrient = {
id = 1004;
name = "Total lipid (fat)";
number = 204;
rank = 800;
unitName = g;
};
type = FoodNutrient;
}
);
}
My Structs:
struct Root: Decodable {
let description: String
let foodNutrients: FoodNutrients
}
struct FoodNutrients: Decodable {
// What should go here???
}
From the JSON, it looks like foodNutrients is an array of unnamed objects, each of which has the values amount: String, id: String, and nutrient: Nutrient (which has id, name etc...) However, forgetting the Nutrient object, I can't even parse the amounts.
struct FoodNutrients: Decodable {
let amounts: [String]
}
I don't think its an array of string, but I have no idea what the () in foodNutrients would indicate.
How would I go about parsing this JSON. I'm using Swift 5 and JSONDecoder. To get the JSON I use JSONSerializer, then print out the JSON above.
This is not a JSON. This is a property list in the openStep format.
This is how it can be modelled (use String instead of Int):
struct Root: Decodable {
let description: String
let foodNutrients: [FoodNutrient]
}
struct FoodNutrient: Decodable {
let id: String
let amount: String
let nutrient: Nutrient
}
struct Nutrient: Decodable {
let name: String
let number: String
let rank: String
let unitName: String
}
And then decode it like this:
try PropertyListDecoder().decode(Root.self, from: yourStr)
The () in foodNutrients indicates that it holds an array of objects - in that case FoodNutrient objects. Therefore your root object should look like this:
struct Root: Decodable {
let description: String
let foodNutrients: [FoodNutrient]
}
Now the foodNutrient is except for the nutrient object straightforward:
struct FoodNutrient: Decodable {
let id: Int // <-- in your example it is an integer and in the second object a string, choose the fitting one from the API
let amount: String
let nutrient: Nutrient
}
And the nutrient object should look like this:
struct Nutrient: Decodable {
let name: String
let number: Int
let rank: Int
let unitName: String
}
Using Decodable is a good and easy way to serialize JSON. Hope that helps. Happy coding :)

Xcode confused with IQAir Api Parsing

so I use I am trying to parse through this data:
{"status":"success","data":{"city":"Sunnyvale","state":"California","country":"USA","location":{"type":"Point","coordinates":[-122.03635,37.36883]},"current":{"weather":{"ts":"2020-07-23T00:00:00.000Z","tp":25,"pr":1009,"hu":44,"ws":6.2,"wd":330,"ic":"02d"},"pollution":{"ts":"2020-07-23T00:00:00.000Z","aqius":7,"mainus":"p2","aqicn":2,"maincn":"p2"}}}}
I am trying to get a hold of the aqius result, as well as the tp...
Here is my code right now, I have created these structs:
struct Response: Codable{
let data: MyResult
let status: String
}
struct MyResult: Codable {
let city: String
}
As you can see, I have gotten city, and I can confirm it works because when I get the request and print(json.data.city) it prints "Sunnyvale".
But how would I get the other values? I have been stuck on how to obtain values within the location , current and pollution data structures, how would I do this?
Thanks
There are tools that automatically generate Codable models from json string like: https://app.quicktype.io)
So your base struct models looks like below;
// MARK: - Response
struct Response: Codable {
let status: String
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Codable {
let city, state, country: String
let location: Location
let current: Current
}
// MARK: - Current
struct Current: Codable {
let weather: Weather
let pollution: Pollution
}
// MARK: - Pollution
struct Pollution: Codable {
let ts: String
let aqius: Int
let mainus: String
let aqicn: Int
let maincn: String
}
// MARK: - Weather
struct Weather: Codable {
let ts: String
let tp, pr, hu: Int
let ws: Double
let wd: Int
let ic: String
}
// MARK: - Location
struct Location: Codable {
let type: String
let coordinates: [Double]
}
Decode;
let jsonData = jsonString.data(using: .utf8)!
let model = try? JSONDecoder().decode(Response.self, from: jsonData)

how to loop through structs?

I'm fetching data from coinDesk API to get bitcoin rate related to other currencies, I've created 3 structs to save this data, but it's not possible to loop through the struct to know how many items I have there...
that's my structure:
struct Response: Codable {
var bpi: currencies
}
struct currencies: Codable {
var USD: info
var GBP: info
var EUR: info
}
struct info: Codable {
var code: String
var symbol: String
var description: String
var rate_float: Float
}
To save the data from API I just use:
let jsonData = try JSONDecoder().decode(Response.self, from: data)
It saves the data with no error but, when I try to loop through this data to populate tableViewCells it doesn't work.
what I'm doing know is...
let euro = jsonData.bpi.EUR
let dollar = jsonData.bpi.USD
let gbp = jsonData.bpi.GBP
let infos = [euro,dollar,gbp]
completion(infos)
This is sending the data to my UITableView and populating, but what if I had 500 currencies? it would not be practical at all.. how could I do this in a more effective way?
Thank you in advance for the answers.
Don't put keys instead
struct Response: Codable {
let bpi: [String:Info]
}
struct Info: Codable {
let code: String
let symbol: String
let description: String
let rate_float: Float
}
Then
let jsonData = try JSONDecoder().decode(Response.self, from: data)
print(jsonData.bpi["USD"])
so for all keys
let keys = Array(jsonData.bpi.keys)
let values = Array(jsonData.bpi.values)

How to decode this using JSONDecoder and Swift 4 Decodabale class

I have decoded this using JSONSerialization. But for my own knowledge and the maintenance of my code. I would like to know how to decode this.
This is what I have so far:
let urlString = "site deleted" + "/DataSource/Mobile/?Action=MyProfile&uid=" + uid + "&uuid=" + uuid
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTask(with: url) { (data, _, error) in
if let err = error {
print("Handle MyProfileJSON error: ", err)
}
guard let data = data else {return}
do {
// swift 4.2 but we cant use it right now
let profile = try JSONDecoder().decode(RequestResult.self, from: data)
print(profile)
completion(profile)
} catch let err {
print("Handle Decoder Error: ", err)
}
}.resume()
I'm not too worried about the cases but this is what I have so far. I know the case I use is not the convention, that's why I did this with JSONSerialization so I can use camelCase. If you can help me convert it to camelCase too that would be amazing but my focus is to Decode this using Decodable class. Thanks a lot, guys.
And this are my structs:
struct RequestResult: Decodable {
var Action: String?
var Params: [String: String]?
var DATA: [String: String]?
}
struct Params: Decodable {
var Action_get: String?
var uid_get: String?
}
struct DATA: Decodable {
var Id: String?
var UserCode: String?
var HomePhone: String?
var Mobile: String?
var WorkPhone: String?
var Email: String?
var AltEmail: String?
var UnitNo: String?
var StreetNo: String?
var StreetName: String?
var City: String?
var StateProvince: String?
var Country: String?
var ZipPostalCode: String?
}
The structure of the JSON is very clear
The root object RequestResult contains a string and two dictionaries.
The dictionaries are replaced by structs.
The CodingKeys are useful to rename the keys to more meaningful and naming convention conforming names. The left side of an enum case is the struct member name, the right side the original JSON key.
A struct member name must match the dictionary key (or the mapped CodingKey).
The struct names are arbitrary.
All struct members can be declared as constants (let) and as non-optional if the JSON contain always the keys.
struct RequestResult: Decodable {
private enum CodingKeys : String, CodingKey {
case action = "Action", params = "Params", data = "DATA"
}
let action: String
let params: Parameter
let data: UserData
}
The dictionary for key Params will be renamed to Parameter and DATA to UserData
struct Parameter: Decodable {
private enum CodingKeys : String, CodingKey {
case action = "Action_get", uid = "uid_get"
}
let action: String
let get: String
}
struct UserData: Decodable {
private enum CodingKeys : String, CodingKey {
case id = "Id", userCode = "UserCode", homePhone = "HomePhone"
case mobile = "Mobile", workPhone = "WorkPhone", email = "Email"
case altEmail = "AltEmail", unitNo = "UnitNo", streetNo = "StreetNo"
case streetName = "StreetName", city = "City", stateProvince = "StateProvince"
case country = "Country", zipPostalCode = "ZipPostalCode"
}
let id: String, userCode, homePhone, mobile: String
let workPhone, email, altEmail, unitNo: String
let streetNo, streetName, city, stateProvince: String
let country, zipPostalCode: String
}