JSON Array parsing to Swift Codeable [closed] - json

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
How would I parse this JSON into Codeable struct. I am also using Moya library to make request
{
"categories": [
{
"categories": {
"id": 1,
"name": "Delivery"
}
},
{
"categories": {
"id": 2,
"name": "Dine-out"
}
},
{
"categories": {
"id": 3,
"name": "Nightlife"
}
}
]
}
Right now my Codeable struct looks like below:
struct Response: Codable {
struct Category: Codable {
let id: Int
let name: String
}
var categories: Category
}
I am gettig an error saying Expected to decode Array but found a Dictionary instead.
UPDATE
provider.request(.categories) { (result) in
// guard let self = self else { return }
switch result {
case .success(let response):
let data = response.data
//let json = try? JSON(data: data)
// swiftlint:disable:next force_try
let test = try! JSONDecoder().decode([Respo].self, from: data)
print(test)
// self.collectionView.reloadData()
case .failure(let error):
print(error)
}
}

Reason:
In you model Response, categories is of type Category. But as per your JSON response, categories is an array of dictionary. That's the reason it is giving Expected to decode Array but found a Dictionary instead
Solution:
categories in Response model must be of type [[String:Category]] instead.
So, The models for parsing the above data should be,
struct Response: Codable {
let categories: [[String:Category]]
}
struct Category: Codable {
let id: Int
let name: String
}
Edit:
Parse it like,
do {
let response = try JSONDecoder().decode(Response.self, from: data)
print(response)
} catch {
print(error)
}

Related

SwiftUI: how to add type for JSON with 0 index array key

I am making a API call in swift and the response of the API looks like this:
[
{
"response": {
"data": {
"first": "Hello",
"second": "Hola",
"third": "Namaste"
}
}
}
]
Now to be able to integrate it into my app I need to decode the response from the API call for which the type I am using is as below:
struct APIResponse: Codable {
let response: Response
}
struct Response: Codable {
let data: Data
}
struct Data: Codable {
let first: String
let second: String
let third: String
}
Now the issue is this type is wrong because the API response is actually a array with only one element (index 0), which means the above type would work if the response from API was
[
"response": {
"data": {
"first": "Hello",
"second": "Hola",
"third": "Namaste"
}
}
]
how to define index-0 data in my type?
The code I am using to make API call and decode JSON:
var request = URLRequest(url: URL(string: "https://myurl.com")!)
let (data, _) = try await URLSession.shared.data(for: request)
let decodedResponse = try? JSONDecoder().decode(APIResponse.self, from: data)
Your response is an array of elements:
let decodedResponse = try JSONDecoder().decode([APIResponse].self, from: data)
and:
let element = decodedResponse.first
should get you going.
And never use try? it will obfuscate all errors.

Swift The data couldn’t be read because it isn’t in the correct format

I am new to swift and trying to figure out how to parse JSON to a struct. I am trying to get an image from NASA Mar's Rover Photos.I am trying to follow tutorials online, but can't seem to fix this issue. What am I doing wrong here?
error:
Fatal error: The data couldn’t be read because it isn’t in the correct format.
import Foundation
class API {
class func getImage(_ onSucessus: #escaping ([LatestPhoto]) -> ()){
Constrant.session.dataTask(with: Constrant.request){(data, res, err) in
guard let data = data, err == nil else{
fatalError()
}
do{
let apod = try Constrant.decoder.decode([LatestPhoto].self, from: data)
DispatchQueue.main.async {
onSucessus(apod)
}
}
catch{
fatalError(error.localizedDescription)
}
}.resume()
}
}
Struct
struct LatestPhoto: Identifiable, Codable{
let id = UUID()
let imgSrc: String
let earthDate: String
enum CodingKeys: String, CodingKey {
case imgSrc = "img_src"
case earthDate = "earth_date"
}
}
JSON
{
"latest_photos": [
{
"id": 839114,
"sol": 3127,
"camera": {
"id": 20,
"name": "FHAZ",
"rover_id": 5,
"full_name": "Front Hazard Avoidance Camera"
},
"img_src": "https://mars.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/03127/opgs/edr/fcam/FRB_675093431EDR_F0880366FHAZ00302M_.JPG",
"earth_date": "2021-05-23",
"rover": {
"id": 5,
"name": "Curiosity",
"landing_date": "2012-08-06",
"launch_date": "2011-11-26",
"status": "active"
}
},
{
"id": 839115,
"sol": 3127,
"camera": {
"id": 20,
"name": "FHAZ",
"rover_id": 5,
"full_name": "Front Hazard Avoidance Camera"
},
"img_src": "https://mars.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/03127/opgs/edr/fcam/FLB_675093431EDR_F0880366FHAZ00302M_.JPG",
"earth_date": "2021-05-23",
"rover": {
"id": 5,
"name": "Curiosity",
"landing_date": "2012-08-06",
"launch_date": "2011-11-26",
"status": "active"
}
}
]
}
Your JSON format doesn't quite match what you're trying to decode. You need a wrapper for the latest_photos array at the root of your JSON object.
For example:
struct LatestPhotosWrapper: Codable {
let latestPhotos: [LatestPhoto]
enum CodingKeys: String, CodingKey {
case latestPhotos = "latest_photos"
}
}
let apod = try JSONDecoder().decode(LatestPhotosWrapper.self, from: data)
(Rather than providing a CodingKey, you can also look into the built-in systems for converting from snake case: https://developer.apple.com/documentation/foundation/jsondecoder/keydecodingstrategy/convertfromsnakecase)
Also, you may want to print the error and not just the error.localizedDescription -- you can get a better picture of what's going on. For example, with your original code, you get:
Expected to decode Array but found a dictionary instead.
Finally, you might check out app.quicktype.io -- you can paste in your JSON and get correct Swift structs pre-built for you.

Parsing a specific content out of a dictionary in a json file

I'm getting the error "Cannot cast value of type dictionary to array" when I run my code. I'm tryna parse from a json file but its not working. Please help.
Here's the json file and i'm specifically tryna print out just the name values:
{
"data": [
{
"id": "2f4628f7ef6dce33d856121f",
"name": "Top products",
"description": "This category contains all the top products on"
},
{
"id": "3f4628f7ef6dce33d856121d",
"name": "Trending products",
"description": "This category contains all the trending products"
}
],
"page": 0,
"size": 2,
"count": 2,
"status": 200,
"isSuccess": true
}
func fetchFrontPageSections() {
let urlString = APIConstants.baseurl + APIConstants.frontpageSections
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
for dictionary in jsonData as! [[String: AnyObject]] {
print(dictionary["name"])
}
} catch let jsonError {
print(jsonError)
}
}.resume()
}
When I print the dictionary I'm getting that whole son file in my console which is fine but Im expecting to get a result like:
name: Top Products
name: Trending Products
In your constant jsonData you don't have an array, it's a dictionary as your json file explicitly show it. It contains multiple keys such as data, page, size, ...
You shoud access first the value related to the key data that contains an at this point the array of dictionary that you are looking for.
Parsing Json in Swift has never been easier since Codable.
here's an Excellent tutorial from the great guys at Ray Wenderlich: https://www.raywenderlich.com/3418439-encoding-and-decoding-in-swift
for your specific problem try this:
struct Info: Decodable {
let id: String
let name: String
let description: String
}
struct Dictionary: Decodable {
let data: [Info]
let page: Int
let size: Int
let count: Int
let status: Int
let isSuccess: Bool
}
let decoder = JSONDecoder()
let dictionary = try decoder.decode(Dictionary.self, from: json)

How to parse Array of JSON to array in Swift4 using alamo fire or swiftjson [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I need to get all the "students_id" value in the below json and store that in an array.Please tell me the efficient way to do this.Please help.Thanks
{
"status": "success",
"user": [
{
"student_id": 1,
"first_name": "Student 1",
"last_name": "Student 1",
"emergency_contact_person": null,
"dob": "0000-00-00",
"class_section_id": 1,
"class_section_name": "A",
"class_id": 1,
"class_name": "10th"
},
{
"student_id": 2,
"first_name": "Student 2",
"last_name": "Student 2",
"emergency_contact_person": null,
"dob": "0000-00-00",
"class_section_id": 1,
"class_section_name": "A",
"class_id": 1,
"class_name": "10th"
}
],
"response": 200
}
You could make use of the Codable protocol. Just create a struct that mirrors the structure if your JSON return:
struct Response: Codable {
let status: String
let user: [Student]
}
struct Student: Codable {
let student_id: Int
let first_name: String
let last_name: String
let emergency_contact_person: String?
let dob: String
let class_section_id: Int
let class_section_name: String
let class_id: Int
let class_name: String
}
From there, you'd use JSONDecoder to decode your JSON and then extract the IDs you need from it using map:
let jsonData = json.data(using: .utf8)
let decoder = JSONDecoder()
var studentIDs: [Int] = []
if let jsonData = jsonData {
do {
let responseStruct = try decoder.decode(Response.self, from: jsonData)
studentIDs = responseStruct.user.map{$0.student_id}
} catch {
print("\(error): \(error.localizedDescription)")
}
}
Here's a link to a post re: decoding JSONs in Swift 4.
If you don't need everything from the JSON, then you could just abbreviate your structs to parse only the elements you need. In this example, you could abbreviate your struct to something like this:
struct Response: Codable {
let user: [Student]
}
struct Student: Codable {
let student_id: Int
}
You could always use a for loop and iterate through each section under user
for string["users"].each do |x| # => string has your PARSED json
return x["student_id"]
end
The code is written in ruby but you will have a basic idea of what to do.

Parsing JSON response in Swift 3

I've got an API endpoint that returns JSON in the following format:
[
{
"id": "1",
"name": "John"
},
{
"id": "2",
"name": "Jane"
},
{
"id": "3",
"name": "Nick"
}
]
I am trying to parse this in Swift 3, but I can only find examples to parse JSON formatted like so:
{
"blogs": [
{
"needspassword": true,
"id": 73,
"url": "http://remote.bloxus.com/",
"name": "Bloxus test"
},
{
"needspassword": false,
"id": 74,
"url": "http://flickrtest1.userland.com/",
"name": "Manila Test"
}
],
"stat": "ok"
}
, which has an extra level above what mine does.
So, where examples I've seen are simply parsing their data like jsonResponse["blogs"], I can't do that as my format is different.
How can I parse the format I've got, or how can I return a format that is easier to parse?
Any suggestions appreciated, thanks!
You can just do the following :
let data = // Data received from WS
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [[String : String]]
//json is now an array from dictionary matching your model
}
catch {
//handle error
}
This will parse it when placed in the network call.
do {
let json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions()) as! [[String : AnyObject]]
let firstPerson = json[0]
print(firstPerson)
let id = firstPerson["id"] as! String
print(id)
let name = firstPerson["name"] as! String
print(name)
} catch {
//handle error
}
Also, I tend to be against advising third party libraries, but SwiftyJSON is an exception I make. If you want to try it, add this to your pod file:
pod SwiftyJSON', '3.0.0'
Documentation: https://github.com/SwiftyJSON/SwiftyJSON
EDIT - Answering Comment:
Replacement line:
if let id = firstPerson["id"] as? String {
print(id)
}
Replacement line (if you need to hold on to the value):
var thisId: String?
if let id = firstPerson["id"] as? String {
thisId = id
}
print(thisId ?? "")
i don't really know swift but there might be the equivalent of ajax json encoding (server side you json_encode your array and client side you json_decode the response)
the idea is to have the same formatter that encodes and decodes the data