Struggling to decode a JSON - ios

I have this json I get from an API:
{
"product_id": "id",
"name": "name",
"manufacturer": "manufacturer",
"image_url": "url",
"additional_info": "",
"store_id": "id",
"store_name": "name",
"owner_id": "id",
"quantities": [
{
"ml": 1,
"price": 2
},
{
"ml": 1,
"price": 2
},
{
"ml": 1,
"price": 2
},
{
"ml": 1,
"price": 2
}
]
}
This is the model I have:
struct Quantity: Codable {
var ml: Int
var price: Float
enum CodingKeys : String, CodingKey {
case ml = "ml"
case price = "price"
}
}
struct Product: Codable {
let uuid: String
let productName: String
let productManufacturer: String
let productImage: String
let quantities: [Quantity]
let additionalInfo: String?
let storeID: String
let storeName: String
let ownerID: String
enum CodingKeys : String, CodingKey {
case uuid = "product_id"
case productName = "name"
case productManufacturer = "manufacturer"
case productImage = "image_url"
case quantities = "quantities"
case additionalInfo = "additional_info"
case storeID = "store_id"
case storeName = "store_name"
case ownerID = "owner_id"
}
}
I'm trying to decode this, but I get this error:
Failed to decode json: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
This is how I decode the JSON:
func fetchGenericData<T: Decodable>(request: URLRequest, completion: #escaping(T) -> ()) {
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
//TODO: Handle error.
print(error)
return
}
guard let data = data else { return }
do {
let object = try self.decoder.decode(T.self, from: data)
completion(object)
}catch let jsonError{
print("Failed to decode json: ", jsonError)
}
}.resume()
}
I understand the error, but I don't know how to fix it.
I don't know what is wrong here, I thought maybe the Quantity, but, to me, it looks like it should decode it just fine, just as "product_id" has value, so each quantity object has it's key and it's value, that doesn't look like a dictionary to me.
Is the problem even in the Quantity object?

My guess is You seem to be trying to decode an array of products but actually it only returning a dictionary that contains only one product. Try this code which tries to decode your JSON into a single product and it should fix the issue:
fetchGenericData(request: request) { (product: Product) in
print(product)
}
Instead of what you might be doing right now which could look like this:
fetchGenericData(request: request) { (products: [Product]) in
print(product)
}

You should use try JSONDecoder().decode(Product.self, from: data)
I think you pass [Product].self

Related

swift unable to use api data in collection View

here my JSON is -
{
"status": "success",
"data": [
{
"_id": "15756323",
"name": "A",
"icons": "https://sdfgsuew34j.png",
"createdAt": "2021-03-18T13:06:44.054Z",
"updatedAt": "2021-03-18T13:06:44.054Z",
"__v": 0,
"id": "6053503drm476"
},
{
"_id": "45646054821",
"name": "S",
"icons": "https://dkj/djf.png",
"createdAt": "2021-03-19T10:51:07.066Z",
"updatedAt": "2021-03-19T10:51:07.066Z",
"__v": 0,
"id": "6054821b11kd6873"
}
]
}
I'm trying to print API data in collectionView
here's some code
var places: [Post]?
func apicall() {
let url = URL(string: "http://17.550.457.84/api/category/list")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil {
do {
self.places = try JSONDecoder().decode([Post].self, from: data! )
} catch {
print("Error during JSON serialization: \(error.localizedDescription)")
}
}
}.resume()
}
I get a message in the debugger -> Error during JSON serialization: The data couldn’t be read because it isn’t in the correct format.
i also try to change -> self.places = try JSONDecoder().decode([Post].self, from: data! )` to
self.places = try JSONDecoder().decode(Post.self, from: data! )
then I got the error -> Cannot assign the value of type 'Post' to type '[Post]?'
here my model class is
struct Post : Codable {
let _id : String?
let name : String?
let icons : String?
let createdAt : String?
let updatedAt : String?
let __v : Int?
let id : String?
enum CodingKeys: String, CodingKey {
case _id = "_id"
case name = "name"
case icons = "icons"
case createdAt = "createdAt"
case updatedAt = "updatedAt"
case __v = "__v"
case id = "id"
}
The error message basically says "Your model and coming data model(JSON) are not equal". You need to change your model like :
struct BaseModel : Codable{
var status : String?
var data : [Post]?
}
And when you try to deserialize it you need to make a instance of that BaseModel so
var baseModel: BaseModel?
var places: [Post]?
when response success
do {
let responseData = try JSONDecoder().decode(BaseModel.self, from: data! )
self.places = responseData.data!
} catch {
print("Error during JSON serialization: \(error.localizedDescription
}

Need Help Parsing Nested JSON Data

I need help parsing this JSON
{
"products_and_categories" : {
"new" : [
{
"id" : 173577,
"image_url" : "\/\/assets.supremenewyork.com\/193786\/ca\/tMUMOnomKJI.jpg",
"name" : "Penguins Hooded Fleece Jacket",
"price" : 19800,
"sale_price" : 0,
"image_url_hi" : "\/\/assets.supremenewyork.com\/193786\/rc\/tMUMOnomKJI.jpg",
"new_item" : true,
"position" : 4,
"category_name" : "Jackets"
},
{
"id" : 173581,
"image_url" : "\/\/assets.supremenewyork.com\/193727\/ca\/ywdDy1mQ51Q.jpg",
"name" : "Side Logo Track Jacket ",
"price" : 15800,
"sale_price" : 0,
"image_url_hi" : "\/\/assets.supremenewyork.com\/193727\/rc\/ywdDy1mQ51Q.jpg",
"new_item" : true,
"position" : 3,
"category_name" : "Jackets"
}
]
}
}
I have made this struct for saving the values that are parsed
var modelsArray: [Products] = Array() //is Products the right one for the array?
struct Products: Codable {
let products: Categories
enum CodingKeys: String, CodingKey {
case products = "products_and_categories"
}
}
struct Categories: Codable {
let new: [New]
}
struct New: Codable {
let id: Int
let name: String
let price: Int
let category: String
let image: String
enum CodingKeys: String, CodingKey {
case id
case name, price
case category = "category_name"
case image = "image_url_hi"
}
}
let url = URL(string: "https://www.supremenewyork.com/shop.json")!
let request = URLRequest(url: url)
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if data = data {
// Having trouble with what to put here to append id, name, price, category, image for tableview
}
}.resume()
But now i'm stuck, i am having trouble converting this into the request to append the data i need. Essentially i want to be able to put id, name, price, category, image into a tableview cell
As I might assume, you must be using the new array to load the data in your tableView. So, you need to define your modelsArray like,
var modelsArray: [New] = Array()
Then parse the JSON data like so,
session.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
let response = try JSONDecoder().decode(Products.self, from: data)
modelsArray = response.products.new
//reload your tableView here...
} catch {
print(error)
}
}
}.resume()

How to parse nested json? Swift

i have a local json string and i am trying to parse it but when i try to do so, i am constantly getting an error. I have also seen this error in nested dictionary but couldnt find an error.
Below is the json string
let jsonNestedString = "{\"meta\":{\"page\":1,\"total_pages\":4,\"per_page\":10,\"total_records\" : 38}, \"reweries\":[\"id\":1234,\"name\":\"saintArnold\"},{\"id\":52892,\"name\":\"buffalo bayou\"]}"
i am doing this process via Codable and below is the struct i have created for the same
struct PagedBreweries:Codable{
struct Meta:Codable{
let page : Int
let total_pages:Int
let per_page:Int
let total_records: Int
enum CodingKeys: String, CodingKey{
case page
case total_pages
case per_page
case total_records
}
}
struct Brewery :Codable{
let id:Int
let name:String
}
let meta:Meta
let breweries :[Brewery]
}
then this data is parsed to a function as below
func jsonNested(){
let jsonData = jsonNestedString.data(using: .utf8)
let decoder = JSONDecoder()
let data = try! decoder.decode(PagedBreweries.Meta.self, from: jsonData!)
print(data)
}
and when i try to build, the error i get is present in try! decoder.decode
command
and the error is
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "page", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"page\", intValue: nil) (\"page\").", underlyingError: nil))
Could any one provide a solution?
Thanks in advance
Correct json
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"reweries": [{
"id": 1234,
"name": "saintArnold"
},
{
"id": 52892,
"name": "buffalo bayou"
}
]
}
struct Root: Codable {
let meta: Meta
let reweries: [Rewery]
}
struct Meta: Codable {
let page, totalPages, perPage, totalRecords: Int
enum CodingKeys: String, CodingKey { // snake case may be used
case age = "page"
case totalPages = "total_pages"
case perPage = "per_page"
case totalRecords = "total_records"
}
}
struct Rewery: Codable {
let id: Int
let name: String
}
let jsonNestedString = """
{\"meta\":{\"page\":1,\"total_pages\":4,\"per_page\":10,\"total_records\":38}, \"reweries\":[{\"id\":1234,\"name\":\"saintArnold\"},{\"id\":52892,\"name\":\"buffalo bayou\"}]}
"""
// or
let jsonNestedString = """
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"reweries": [{
"id": 1234,
"name": "saintArnold"
},
{
"id": 52892,
"name": "buffalo bayou"
}
]
}
"""
do {
let jsonData = jsonNestedString.data(using: .utf8)
let decoder = JSONDecoder()
let data = try decoder.decode(Root.self, from: jsonData!)
print(data)
} catch {
print(error)
}
The JSON is corrupt and you are decoding the wrong root object.
This is the correct JSON being decoded with your structs and conforming to the Swift naming convention
let jsonNestedString = """
{"meta":{"page":1,"total_pages":4,"per_page":10,"total_records" : 38}, "breweries":[{"id":1234,"name":"saintArnold"},{"id":52892,"name":"buffalo bayou"}]}
"""
struct PagedBreweries : Codable {
struct Meta : Codable {
let page : Int
let totalPages:Int
let perPage:Int
let totalRecords: Int
}
struct Brewery : Codable {
let id:Int
let name:String
}
let meta:Meta
let breweries :[Brewery]
}
func jsonNested(){
let jsonData = Data(jsonNestedString.utf8)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let data = try! decoder.decode(PagedBreweries.self, from: jsonData)
print(data)
}
jsonNested()
// Printed result:
// PagedBreweries(meta: __lldb_expr_1.PagedBreweries.Meta(page: 1, totalPages: 4, perPage: 10, totalRecords: 38), breweries: [__lldb_expr_1.PagedBreweries.Brewery(id: 1234, name: "saintArnold"), __lldb_expr_1.PagedBreweries.Brewery(id: 52892, name: "buffalo bayou")])

Swift ObjectMapper: How to parse array inside of an array

This is my JSON response:
[
[
{
"id": 22,
"request_id": "rqst5c12fc9e856ae1.06631647",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Apache Load/Ubuntu",
}
],
[
{
"id": 24,
"request_id": "rqst5c130cae6f7609.41056231",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
}
]
]
This JSON structure got an array inside of an array, the object of the inner array is what I am trying to parse. Here is the my mapper:
struct JobResponseDataObject: Mappable {
init?(map: Map) {
}
var id: Int?
var requestId: String?
var businessName: String?
var businessEmail: String?
mutating func mapping(map: Map) {
id <- map["id"]
requestId <- map["request_id"]
businessName <- map["business_name"]
businessEmail <- map["business_email"]
}
}
I have tried create another mapper struct to hold the array of objects [JobResponseDataObject] and use Alamofire's responseArray with it, but it didn't work. I have also tried prefixing my json id with 0. but that didn't work too. Please help
Thank
So here's the deal...Codable is a pretty cool protocol from Apple to handle parsing JSON responses from APIs. What you're getting back is an array of arrays, so your stuff's gonna be look like this:
[[ResponseObject]]
So anyway, you'd make a struct of your object, like so:
struct ResponseObject: Codable {
let id: Int?
let requestId: String?
let businessName: String?
let businessEmail: String?
let title: String?
}
You'll note I changed the key name a bit (instead of request_id, I used requestId). The reason is JSONDecoder has a property called keyDecodingStrategy which presents an enum of canned decoding strategies you can select from. You'd do convertFromSnakeCase.
Here's code you can dump into a playground to tinker with. Basically, declare your struct, match it up to whatever the keys are in your JSON, declare a decoder, feed it a decoding strategy, and then decode it.
Here's how you could do an Alamofire call:
private let backgroundThread = DispatchQueue(label: "background",
qos: .userInitiated,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil)
Alamofire.request(url).responseJSON(queue: backgroundThread) { (response) in
guard response.result.error == nil else {
print("💥KABOOM!💥")
return
}
if let data = response.data {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let parsedResponse = try decoder.decode([[ResponseObject]].self, from: data)
print(parsedResponse)
} catch {
print(error.localizedDescription)
}
}
}
Here's code you can chuck in a playground.
import UIKit
let json = """
[
[
{
"id": 22,
"request_id": "rqst5c12fc9e856ae1.06631647",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Apache Load/Ubuntu",
}
],
[
{
"id": 24,
"request_id": "rqst5c130cae6f7609.41056231",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
}
]
]
"""
struct ResponseObject: Codable {
let id: Int?
let requestId: String?
let businessName: String?
let businessEmail: String?
let title: String?
}
if let data = json.data(using: .utf8) {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let parsedResponse = try decoder.decode([[ResponseObject]].self, from: data)
print(parsedResponse)
} catch {
print(error.localizedDescription)
}
}
You should use this JobResponseDataObject struct as [[JobResponseDataObject]] instead of [JobResponseDataObject] - where you are making a property using this struct in your parent struct or class.
You can use Codable here for mapping the JSON response, The JobResponseDataObject struct should look like,
struct JobResponseDataObject: Codable {
var id: Int?
var requestId: String?
var businessName: String?
var businessEmail: String?
var title: String?
private enum CodingKeys: String, CodingKey {
case id = "id"
case requestId = "request_id"
case businessName = "business_name"
case businessEmail = "business_email"
case title = "title"
}
}
let json = JSON(responseJSON: jsonData)
do {
if let value = try? json.rawData(){
let response = try! JSONDecoder().decode([[JobResponseDataObject]].self, from: value)
}
} catch {
print(error.localizedDescription)
}

Swift 4 - Nesting JSON Response for Class Structure into TableViewCell

I've got the following structures to aid in returning data from a JSON web api:
// To parse the JSON, add this file to your project and do:
//
// let story = try? JSONDecoder().decode(Story.self, from: jsonData)
import Foundation
typealias Story = [StoryElement]
struct StoryElement: Codable {
let id: Int
let url: String
let storyPublic, featured: Bool
let added, modified: String
let itemType: ItemType
let collection: JSONNull?
let owner: Owner
let files: Files
let tags: [ItemType]
let elementTexts: [ElementText]
let extendedResources: ExtendedResources
enum CodingKeys: String, CodingKey {
case id, url
case storyPublic = "public"
case featured, added, modified
case itemType = "item_type"
case collection, owner, files, tags
case elementTexts = "element_texts"
case extendedResources = "extended_resources"
}
}
struct ElementText: Codable {
let html: Bool
let text: String
let elementSet: ElementSet
let element: Element
enum CodingKeys: String, CodingKey {
case html, text
case elementSet = "element_set"
case element
}
}
struct Element: Codable {
let id: Int
let url, name: String
let resource: ElementResource
}
enum ElementResource: String, Codable {
case elements = "elements"
}
struct ElementSet: Codable {
let id: Int
let url: URL
let name: Name
let resource: ElementSetResource
}
enum Name: String, Codable {
case dublinCore = "Dublin Core"
case itemTypeMetadata = "Item Type Metadata"
}
enum ElementSetResource: String, Codable {
case elementSets = "element_sets"
}
enum URL: String, Codable {
case httpWWWRalstoncemeteryCOMGreeleyAPIElementSets1 = "http://www.ralstoncemetery.com/greeley/api/element_sets/1"
case httpWWWRalstoncemeteryCOMGreeleyAPIElementSets3 = "http://www.ralstoncemetery.com/greeley/api/element_sets/3"
}
struct ExtendedResources: Codable {
let exhibitPages: Files
let geolocations: Owner
enum CodingKeys: String, CodingKey {
case exhibitPages = "exhibit_pages"
case geolocations
}
}
struct Files: Codable {
let count: Int
let url, resource: String
}
struct Owner: Codable {
let id: Int
let url, resource: String
}
struct ItemType: Codable {
let id: Int
let url, name, resource: String
}
// MARK: Encode/decode helpers
class JSONNull: Codable {
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
You can see the JSON output I am working with, as well as the original structures here: https://app.quicktype.io?share=oAMNjooSzgpraWpf1KIj (just for reference).
The data (I believe) is returned successfully and parsed correctly, the only issue comes down to myself not being as familiar (I'm in the process of trying to learn a bit more).
So I have a TableViewController and in that I have a cell with the following coded into it:
struct StoryCellViewModel {
let id: Int
let url: String
let storyPublic, featured: Bool
let added, modified: String
}
And in the actual TableViewController under the viewDidLoad() I have this portion of script:
print(story)
self.cellViewModels = story.map{
StoryCellViewModel(id: $0.id, url: $0.url, storyPublic: $0.storyPublic, featured: $0.featured, added: $0.added, modified: $0.modified)
}
and a little bit lower than that, I have:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "StoryCell", for: indexPath)
let cellViewModel = cellViewModels[indexPath.row]
cell.textLabel?.text = String(cellViewModel.id)
cell.detailTextLabel?.text = cellViewModel.modified
return cell
}
I will make it known that all of this works, the id and modified (both just being used as tests to ensure the connection and printing works properly) are fine, but my question comes up here:
If we go back to the structures there is this portion:
struct ElementText: Codable {
let html: Bool
let text: String
let elementSet: ElementSet
let element: Element
enum CodingKeys: String, CodingKey {
case html, text
case elementSet = "element_set"
case element
}
}
Which refers to the following portion of example JSON:
"element_texts": [
{
"html": false,
"text": "Woehler and Force Farm Equipment Building",
"element_set": {
"id": 1,
"url": "http://www.ralstoncemetery.com/greeley/api/element_sets/1",
"name": "Dublin Core",
"resource": "element_sets"
},
"element": {
"id": 50,
"url": "http://www.ralstoncemetery.com/greeley/api/elements/50",
"name": "Title",
"resource": "elements"
}
},
{
"html": false,
"text": "Woehler and Force Farm Equipment",
"element_set": {
"id": 1,
"url": "http://www.ralstoncemetery.com/greeley/api/element_sets/1",
"name": "Dublin Core",
"resource": "element_sets"
},
"element": {
"id": 39,
"url": "http://www.ralstoncemetery.com/greeley/api/elements/39",
"name": "Creator",
"resource": "elements"
}
},
{
"html": false,
"text": "Street view of the front exterior of Woehler and Force Farm Equipment. Several automobiles are visible through the windows of the store. The alley along the side of the building is also visible. There are several signs along the front of the building reading, 'Farm Equipment,' 'Kaiser Frazer W&F,' and 'Woehler & Force.'; Verso There is a sticker with typed black ink reading, 'Woehler & Force 1316-22 8th Ave. - Greeley, CO 1947. Orig. env. says Liberty Trucker Parts Co./ 690 Lincoln St./F.V. Altwater/POB 1889/Denver, Colo.'",
"element_set": {
"id": 3,
"url": "http://www.ralstoncemetery.com/greeley/api/element_sets/3",
"name": "Item Type Metadata",
"resource": "element_sets"
},
"element": {
"id": 54,
"url": "http://www.ralstoncemetery.com/greeley/api/elements/54",
"name": "Story",
"resource": "elements"
}
}
],
**So my question is: ** How would I go about printing out say the first of the element_texts (The one that has "Woehler and Force Farm Equipment Building" set for the text field)?
If there is any further explanation required, I'll be happy to type it up. Or if anyone has any resources for this level of nesting, I'd be very grateful. Thank you -
story is an array of StoryElement (why not stories?)
The type of property elementTexts in StoryElement is an array of ElementText
So basically you need two loops to iterate over story and elementTexts
for aStory in story {
for elementText in aStory.elementTexts {
print(elementText.text)
}
}
First of all in "CellForRowat" call let cell = dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath etc.
then place text in cell.text
Create a array like this . var AllTexts = [String]()
you can fetch json data so many ways like JsonDecode/JSONSerialization, i am using JSONSerialization. After fetching data i am appending text values in to array.
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as! [String: Any]
for (key,value) in jsonResult {
print(key)
if let result2:[[String:Any]] = value as? [[String:Any]]{
for dict in result2 {
for (key,value) in dict {
if key == "text" {
self.AllTexts.append(value as! String)
}
}
}
}
}
print(self.AllTexts)
} catch {
// handle error
print(error)
}

Resources