Grab data from nested JSON - json

Trying to grab the below JSON data. I can pull the everything but the foodNutrients. When running the below coded, I receive the following
Result:
MealJournal.FoodNutrients(foodNutrients: nil)
{
"fdcId":748967,
"description":"Eggs, Grade A, Large, egg whole",
"publicationDate":"12/16/2019",
"foodNutrients":[
{
"type":"FoodNutrient",
"nutrient":{
"id":1091,
"number":"305",
"name":"Phosphorus, P",
"rank":5600,
"unitName":"mg"
},
How would I go about grabbing foodNutrients?
I have the below code and I am getting the following results
Code:
struct FoodNutrients: Codable{
let foodNutrients: [String]?
enum CodingKeys: String, CodingKey{
case foodNutrients = "foodNutrients"
}
}
struct Food: Codable{
let dataType: String
let description: String
let fdcId: Int
let foodNutrients: [FoodNutrients]
}
class FoodApi {
func getFood (){
guard let url = URL(string: "https://api.nal.usda.gov/fdc/v1/food/748967?api_key=bRbzV0uKJyenEtd1GMgJJNh4BzGWtDvDZVOy8cqG") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
let results = try! JSONDecoder().decode(Food.self, from: data!)
print(results)
}
.resume()
}
}

The food nutrients struct is wrong and needs to match the json. Use https://app.quicktype.io/ to generate the proper classes/structs

Related

How to extract the result of json with Codable

I'm using Codable for the first time and want to output the json result of Google Places details as a label.
However, when I print it, the console says "The data could n’t be read because it isn’t in the correct format.”.
I can't solve it by myself, so please tell me how to write it correctly.
Thanks.
The result of json
{
"html_attributions": [],
"result": {
"formatted_phone_number": "XXXX-XXX-XXX",
"website": "https://www.xxxxx.com/xxxxxx/"
},
"status": "OK"
}
Detail.swift
import Foundation
struct Details : Codable {
var formatted_phone_number : String!
var website : String!
}
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
fetchDetailData {(details) in
for detail in details{
print(detail.website)
}
}
}
func fetchDetailData(completionHandler: #escaping ([Details]) -> Void){
let url = URL(string: "https://maps.googleapis.com/maps/api/place/details/json?place_id=\(place_id)&fields=formatted_phone_number,website&key=\(apikey)")!
let task = URLSession.shared.dataTask(with: url){
(data,respose, error)in
guard let data = data else{ return }
do {
let detailsData = try JSONDecoder().decode([Details].self, from: data)
completionHandler(detailsData)
}
catch{
let error = error
print(error.localizedDescription)
}
}.resume()
}
One of the issues there is that result is a dictionary not an array. You need also to decode the root structure to extract the result from it. Note that you can also change the website type from String to URL:
struct Root: Codable {
let htmlAttributions: [String] // make sure to define the proper type in case the collection is not empty
let result: Result
let status: String
}
struct Result: Codable {
let formattedPhoneNumber: String
let website: URL
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Root.self, from: data).result
print(result)
} catch {
print(error)
}
This will print
Result(formattedPhoneNumber: "XXXX-XXX-XXX", website: https://www.xxxxx.com/xxxxxx/)
could you try this;
struct Place {
let result: Details?
}
struct Details: Codable {
let phoneNumber: String?
let website: String?
enum CodingKeys: String, CodingKey {
case website
case phoneNumber = "formatted_phone_number"
}
}
and parse Place.self
you will also need to change "#escaping ([Details])" to "#escaping (Place)"

How to parse a multi-level json structure in Swift?

The JSON from server looks like this:
A dictionary where the value is another dictionary.
{
"S1": {
"vpn_status": 2,
"vpn_name": "vpn1"
},
"S2": {
"vpn_status": 1,
"vpn_name": "vpn2"
}
}
I have created the following struct to parse it.
public struct ServerStatusResult {
public let vpnName: String
public let status: Int
init?(json: [String: Any]) {
guard
let vpnName = json["vpn_name"] as? String,
let status = json["vpn_status"] as? Int
else {
return nil
}
self.vpnName = vpnName
self.status = status
}
}
And the function to call the server is:
typealias serverStatusCompletedClosure = (_ status: Bool, _ result: Dictionary<String,ServerStatusResult>?, _ error: ServiceError?)->Void
func serverStatus(email: String, password: String, complete: #escaping serverStatusCompletedClosure) {
let url = URL(string: "...")!
try? self.httpClient.get(url: url,
token: "...",
email: email,
password: password)
{ (data, response, error) in
if let error = error {
complete(false, nil, ServiceError.invalidSession)
} else if let httpResponse = response as? HTTPURLResponse {
switch (httpResponse.statusCode) {
case 200:
var result: [String:ServerStatusResult]? = nil
result = try! JSONSerialization.jsonObject(with: data!, options: []) as! Dictionary<String, ServerStatusResult>
complete(true, result, nil)
This is where my json transformation fails.
Could not cast value of type '__NSDictionaryI' (0x7fff8eaee9b0) to
'app.ServerStatusResult' (0x10021dec0).
What am I missing please?
You can solve it by using Decodable and using a dictionary
First make your struct conform to Decodable
public struct ServerStatusResult: Decodable {
public let vpnName: String
public let status: Int
enum CodingKeys: String, CodingKey {
case vpnName = "vpn_name"
case status = "vpn_status"
}
}
and then the decoding is easy
do {
let result = try JSONDecoder().decode([String: ServerStatusResult].self, from: data)
print(result) //or in you case complete(true, result, nil)
} catch {
print(error)
}
You get an array of dictionary [[String: Any]]
Create a struct for a dictionary and if a dictionary has another dictionary inside it then create another struct for the inner Dictionary and create an object in the outer struct for innner dictionary json.
You can use codeable to parse your json easily, by inheriting your struct with codeable

Syntax for accessing struct property with enum type in JSON model

I am trying to access a url string contained within some JSON data.
The string is contained within the "urls" array with type "detail" as can be seen below.
JSON DATA
I used quicktype to construct my model as below:
struct Response: Codable {
let data: DataClass
}
struct DataClass: Codable {
let results: [Result]
}
struct Result: Codable {
let name: String
let description: String
let thumbnail: Thumbnail
let urls: [URLElement]
}
struct Thumbnail: Codable {
let path: String
let thumbnailExtension: Extension
enum CodingKeys: String, CodingKey {
case path
case thumbnailExtension = "extension"
}
}
enum Extension: String, Codable {
case jpg = "jpg"
}
struct URLElement: Codable {
let type: URLType
let url: String
}
enum URLType: String, Codable {
case comiclink = "comiclink"
case detail = "detail"
case wiki = "wiki"
}
I have tried accessing it by declaring it like so...
var urlelement: URLElement!
override func viewDidLoad() {
super.viewDidLoad()
let detailurl = urlelement.url
print(detailurl)
... but it always returns an empty string. Any suggestions will be most welcome. Thanks!
First Download the JSON then user JSONDecoder
let url = URL(string: "your url")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("error \(error.localizedDescription)")
return
}
guard let data = data else { return }
do {
let response = try JSONDecoder().decode(Response.self, from: data)
// use this
response?.data.results.forEach({ (rsl) in
rsl.urls.forEach({ (element) in
print(element.type, element.url)
})
})
// or this one
for rsl in response!.data.results {
for element in rsl.urls {
print(element.type, element.url)
}
}
} catch let error {
print("error while decoding the json \(error.localizedDescription)")
}
}.resume()

How to get value in json Swift

I hava a json file:
jsonpElixo({
"response":{
"parks":[
{
"Park":{
"id":"2",
"name":"PARQUE VILLA-LOBOS",
"type":"Urbano"
},
"Address":{
"lat":"-23.547245206920508",
"long":"-46.71627699999999",
"cep":null,
"street":"Avenida Professor Fonseca Rodrigues",
"number":"1025",
"neighborhood":"Alto Pinheiros",
"city":"S\u00e3o Paulo",
"state":"SP",
"id":"9"
}
}
]
}
})
But I can't get the elements inside the {}. Because the "jsonpElixo(" is breaking during the decodable.
How can I fix that?
The func to get info about json file.
func getParks() {
var request = URLRequest(url:gitUrl)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let welcome = try? decoder.decode(Welcome.self, from: data)
} catch let err {
print("Err", err)
}
}.resume()
}
The structs to Decodable the elements. But I dont know how can I scape this first element ("jsonpElixo(")
struct Welcome: Decodable {
let response: Response
}
struct Response: Decodable {
let parks: [Park]
}
struct Park: Decodable {
let park: ParkClass
let address: Address
enum CodingKeys: String, CodingKey {
case park = "Park"
case address = "Address"
}
}
struct Address: Decodable {
let lat, long: String
let cep: String?
let street, number, neighborhood, city: String
let state, id: String
}
struct ParkClass: Decodable {
let id, name, type: String
}
You can create a function that will remove the outer jsonpElixo() object and return the json to work with.
Start with an extension on Data so we can create something similar to this:
extension Data {
func decodeJsonpElixo() -> Data {
guard let jsonpString = String(data: self, encoding: .utf8) else {return self}
if jsonpString.hasPrefix("jsonpElixo(") && jsonpString.hasSuffix(")") {
var decoderString = jsonpString.replacingOccurrences(of: "jsonpElixo(", with: "")
decoderString.remove(at: String.Index(encodedOffset: decoderString.endIndex.encodedOffset - 1))
return Data(decoderString.utf8)
}
return self
}
}
Then you can use this in your URLSession closure like this:
guard let data = data else { return }
let decoderData = data.decodeJsonpElixo()
let decoder = JSONDecoder()
do {
let welcome = try decoder.decode(Welcome.self, from: decoderData)
} catch let err {
print(err)
}

Convert ID to Username using JSON

I have two JSON call functions that get clients data and invoice data. In my clients JSON response I have ID and name, etc. In my invoice response I have client ID.
I need to get the client ID from invoice and convert to the client name string.
In my ClientsViewController I've made a function called downloadClientsJSON
func downloadClientsJSON(completed: #escaping () -> ()) {
let url = URL(string: "http://ex.com/app/api/clientList/get")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.clients.removeAll()
self.clients = try JSONDecoder().decode([ClientsList].self, from: data!)
DispatchQueue.main.async {
completed()
}
} catch {
print ("Error")
}
}
}.resume()
}
And here is the ClientsList structure:
import Foundation
struct ClientsList:Decodable {
let name: String
let vatNumber: String
let address: String
let cap: String
let city: String
let prov: String
let tel: String
let email: String
}
Here is the JSON Response:
[{"id":1,"name":"DemoClient","vatNumber":"010101010101","address":"Demo Address, 1","cap":"01010","city":"DemoCity","prov":"DP","tel":"555-1112223","email":"demo#example.com"}]
In my InvoiceViewController I've made a function called downloadInvoicesJSON
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "http://ex.com/app/api/invoiceList/get")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.invoices.removeAll()
self.invoices = try JSONDecoder().decode([InvoiceList].self, from: data!)
DispatchQueue.main.async {
completed()
}
} catch {
print (error)
}
}
}.resume()
}
Here is the InvoiceList structure:
import Foundation
struct InvoiceList:Decodable {
let id: NSInteger
let date: String
let expiration: String
let client: NSInteger
let status: NSInteger
let lines: String
let total: Float
}
Here is the JSON Response:
[{"id":1,"date":"01-01-2018","expiration":"01-01-2018","client":3,"status":3,"lines":"1:1","total":700},{"id":2,"date":"30-01-2018","expiration":"30-01-2018","client":4,"status":2,"lines":"1:1","total":100},{"id":3,"date":"15-02-2018","expiration":"15-02-2018","client":3,"status":3,"lines":"1:1","total":700}]
The function should be in the InvoiceViewController. How do I get the result?
JSON object you are getting from http://ex.com/app/api/clientList/get should be an array and parameter keys should be same as property name of ClientsList.
struct ClientsList: Decodable {
let name: String
let vatNumber: String
let address: String
let cap: String
let city: String
let prov: String
let tel: String
let email: String
}
If you are not sure that you will receive all properties in the response JSON, then make that particular property optional. like:
struct ClientsList: Decodable {
let name: String
let vatNumber: String
let address: String
let cap: String
let city: String? // Not sure about receiving this property
let prov: String? // Not sure about receiving this property
let tel: String? // Not sure about receiving this property
let email: String
}
Same pattern you need to follow for http://ex.com/app/api/invoiceList/get and InvoiceList.