Make an array out of JSON response [duplicate] - ios

This question already has answers here:
how to get array from dictionary in swift 3
(2 answers)
Closed 3 years ago.
I have following response to get Areas but I need an Individual array out of this with separator
{
"name" : "Abu Hail",
"city_id" : 1,
"pk" : 227,
"city" : "Dubai"
},
{
"name" : "Academic City",
"city_id" : 1,
"pk" : 184,
"city" : "Dubai"
},
{
"name" : "Al Barari",
"city_id" : 1,
"pk" : 185,
"city" : "Dubai"
},
{
"name" : "Al Barsha 1,2 & 3",
"city_id" : 1,
"pk" : 166,
"city" : "Dubai"
},
How can I make an array from name below out of this
["Abu Hail", "Academic City", "Al Barari", "Al Barsha 1,2 & 3"]
Following are my codes to get the above response
func getAreas(){
let headers: HTTPHeaders = [
"Authorization": "Token \(token!)",
"Accept": "application/json"
]
AF.request("\(staging.url)/api/addresses/areas/", method: .get, encoding: URLEncoding(), headers: headers).responseJSON { (response:DataResponse<Any>) in
switch response.result {
case let .success(value):
let json = JSON(value)
print("Areas Array: \(json)")

Create array using:
if let array = array as? [[String : Any]] {
let namesArray = array.compactMap { $0["name"] } as? [String]
}

It's better to create codable objects
// MARK: - AreaElement
struct AreaElement: Codable {
let name: String?
let cityID, pk: Int?
let city: String?
enum CodingKeys: String, CodingKey {
case name
case cityID = "city_id"
case pk, city
}
}
Now you can directly parse using JSONDecoder like
fileprivate func retrieve<T: Decodable>(data: Data, type: T.Type) -> T? {
let decoder = JSONDecoder()
do {
let model = try decoder.decode(type, from: data)
return model
} catch(let error) {
return nil
}
}
AF.request("\(staging.url)/api/addresses/areas/", method: .get, encoding: URLEncoding(), headers: headers).responseData { (response:DataResponse<Data>) in
if let data = response.data {
let models = self.retrieve(data: data, type: [Area].self)
// How you get name from model with one line
let names = models?.map {$0.name}
}
.......
Hopefully it will be helpful

Related

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()

"Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil

Different api responses, not getting handled in code. Need to parse the array but my code expecting dictionary, I am not getting where to change. Please guide. Below is code:
func fetchProducts() {
print("Page no\(String(describing: filter.page) )")
NetworkHelper().makeAPIRequest(forAPI: .getProducts(forProduct: filter), responseType: ProductsDescription.self) { [weak self] (result) in
guard let `self` = self else { return }
switch result{
case .error(let error):
self.delegate?.ProductListing(self, didGetError: error)
case .success(let response,let responseMsg):
print("success")
guard let resp = response,let products = response?.data else {
return
}
if self.filter.page == 1 {
self.products = []
}
if products.count > 0 {
self.products.append(contentsOf: products)
self.filter.page? += 1
}
self.delegate?.ProductListing(self, didSuccessProductListingDataWithMsg: nil)
self.isInProgress = false
}
}
}
// Updated:
class ProductsDescription: Codable {
var data: [Product]?
enum CodingKeys: String, CodingKey {
case data
}
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let dataJSON = try values.decodeIfPresent(JSONModel.self, forKey: .data)
if let productsData = try dataJSON?.rawData() {
self.data = try JSONDecoder().decode([Product].self, from: productsData)
}
}
}
Api response:
{
"page" : "1",
"data" : [
{
"options" : [
{
"value" : "Black berries",
"key" : 128
}
],
"product_name" : "Fresh",
"old_price" : "$10.00",
"prodt_name" : "Exotic Fruits",
"is_favorite" : 1,
"product_rating" : 0,
"product_id" : "13",
"user_id" : "135",
"brand_id" : "126",
"selprod_id" : "128",
"product_veg_status" : "1",
"user_name" : "raj yadav",
"product_price" : "$10.00",
"prodcat_id" : "211",
"discounted_price" : "-0% Off",
"product_name" : "Black berries",
"special_price_found" : "0",
"selprod_stock" : "1000",
"selprod_min_order_qty" : "1",
"product_model" : "b44"
},
Error:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
In short problem is when response comes as below:
"data" : [
{
If comes this way, it works fine:
"data": {
"products": [

Struggling to decode a JSON

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

How to parse Json response in Swift

I need to parse out "choices" and save 123347 and save "option" to a string from the following String:
{
"type" : "radiobuttons",
"patient" : false,
"text" : "How are you today",
"id" : 63339,
"position" : 2,
"options" : "123344:ok today;123345:see you tomorrow;123346:call friend;123347:call lisa;",
"required" : true,
"choices" : {
"123347" : {
"value" : 3,
"option" : "iOS"
},
"123345" : {
"option" : "Android",
"value" : 1
},
"123346" : {
"option" : "Windows",
"value" : 2
},
"123344" : {
"option" : "MAC",
"value" : 0
}
}
}
let json = try? JSONSerialization.jsonObject(with: str, options: [])
Swift 5
Try to serialize and decode it
let jsonResponse = try JSONSerialization.data(withJSONObject: responseObject as Any, options: JSONSerialization.WritingOptions.sortedKeys)
let customObject = try JSONDecoder().decode(CustomObject.self, from: jsonResponse)
guard let requiredChoice = customObject.choices["123347"] else{
return
}
let option = requiredChoice.option
print(option)
CustomObject for your json:
struct CustomObject: Codable {
let type: String
let patient: Bool
let text: String
let id, position: Int
let options: String
let customObjectRequired: Bool
let choices: [String: Choice]
enum CodingKeys: String, CodingKey {
case type, patient, text, id, position, options
case customObjectRequired = "required"
case choices
}
}
struct Choice: Codable {
let option: String
let value: Int
}
There are many tools available to easily create struct/class for your json:
E.g: https://app.quicktype.io

Parse plain json string value inside a response swift codable

I want to parse this response. Everything else is parsed - except Images. I receive it in a string but then I cannot convert it into a dictionary. This is my model.
struct PropertyList: Decodable {
let result: Property?
let success: Bool = false
struct Property: Decodable {
let name: String?
let description: String?
let propertyType: PropertyType
let latitude, longitude: String
let images: String?
let areaSize:Int?
let threeSixtyView: String?
let threeDModel: String?
enum CodingKeys: String, CodingKey {
case name
case propertyDescription = "description"
case propertyType, latitude, longitude
case threeDModel = "threeDModel"
case images = "images"
}
}
}
struct PropertyType: Codable {
let id: Int
let name, propertyTypeDescription: String
enum CodingKeys: String, CodingKey {
case id, name
case propertyTypeDescription = "description"
}
}
API Response :
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel": "https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images": "[{\"id\": 1, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg\"},\n{\"id\": 2, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg\"},\n{\"id\": 3, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg\"},\n{\"id\": 4, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg\"},\n{\"id\": 5, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg\"},\n{\"id\": 6, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg\"},\n{\"id\": 7, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg\"}]"
> Blockquote
images is a nested JSON string which has to be decoded separately.
A solution is to declare the value containing the nested JSON string as struct (ImageJSON) with a singleValueContainer and decode the string on second level.
I left out the properties which are not in the JSON
let json = """
{
"success" : true,
"result" : {
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel": "https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images":"[{\\"id\\": 1, \\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg\\"},{\\"id\\": 2,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg\\"},{\\"id\\": 3,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg\\"},{\\"id\\": 4,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg\\"},{\\"id\\": 5,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg\\"},{\\"id\\": 6,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg\\"},{\\"id\\": 7,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg\\"}]"
}
}
"""
struct Response : Decodable {
let result: Property
let success: Bool
}
struct Property: Decodable {
let name: String
let description: String
let propertyType: PropertyType
let latitude, longitude: String
let images: ImageJSON
let threeDModel: URL
}
struct PropertyType: Codable {
let id: Int
let name, description: String
}
struct Image : Decodable {
let id : Int
let image : URL
}
struct ImageJSON : Decodable {
let images : [Image]
init(from decoder : Decoder) throws {
let container = try decoder.singleValueContainer()
let imageJSONString = try container.decode(String.self)
let imageJSONData = Data(imageJSONString.utf8)
images = try JSONDecoder().decode([Image].self, from: imageJSONData)
}
}
let data = Data(json.utf8)
do {
let decoder = JSONDecoder()
let response = try decoder.decode(Response.self, from: data)
let images = response.result.images.images
print(images)
} catch {
print(error)
}
Try the below code :
Update your json :
let json = """
{
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel":"https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images": [
{"id": 1, "image": "https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg"},
{"id": 2, "image": "https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg"},
{"id": 3, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg"},
{"id": 4, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg"},
{"id": 5, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg"},
{"id": 6, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg"},
{"id": 7, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg"}
]
}
"""
Update your codable class :
struct Property : Codable {
let descriptionField : String?
let images : [Images]?
let latitude : String?
let longitude : String?
let name : String?
let propertyType : PropertyType?
let threeDModel : String?
let viewOfWater : String?
enum CodingKeys: String, CodingKey {
case descriptionField = "description"
case images = "images"
case latitude = "latitude"
case longitude = "longitude"
case name = "name"
case propertyType = "propertyType"
case threeDModel = "threeDModel"
case viewOfWater = "viewOfWater"
}
}
struct PropertyType : Codable {
let descriptionField : String?
let id : Int?
let name : String?
enum CodingKeys: String, CodingKey {
case descriptionField = "description"
case id = "id"
case name = "name"
}
}
struct Images : Codable {
let id : Int?
let image : String?
}
if let jsonData = json.data(using: .utf8) {
let decoder = JSONDecoder()
do {
let jsonModel = try decoder.decode(Property.self, from: jsonData)
print(jsonModel)
} catch {
print("Error = \(error)")
}
}
Create an image type as below,
struct PropertyImage: Decodable {
var id: Int
var image: String?
}
Now, get data from the images string and decode an array of property images as below,
if let data = property.images.data(using: .utf8) {
do {
let images = try JSONDecoder().decode([PropertyImage].self, from: data)
images.forEach { image in
print(image.id)
print(image.image)
}
} catch {
print(error)
}
}

Resources