swift json parsing based on parameter - json

I am having the following web service response in which I need parse the parameters based on "MainServiceCategories_ID"
Here is the response
{
"Entity": {
"ID": 20021,
"UserTypeID": 1,
"UserType": null,
"UserID": 30046,
"Code": "lPx1lTEq",
"Name": "بدراء",
"EnglishName": "Badra",
"IconProfileImageID": "",
"MainImageProfileImageID": "EED372C3-5C4F-E711-8100-0CC47A343427",
"LocationLng": 78.37021,
"LocationLat": 17.432563,
"Services": [
{
"ID": 11788,
"MainServiceCategories_ID": 3,
"ServiceCategories_ID": 6,
"Children": [
{
"Parent_ID": 6,
"Name": “John”,
"EnglishName": “Johnny”,
},
{
"Parent_ID": 6,
"Name": “Ronny”,
"EnglishName": “Ronny”,
}]
},
{
"ID": 11788,
"MainServiceCategories_ID": 2,
"ServiceCategories_ID": 6,
"Children": [
{
"Parent_ID": 6,
"Name": “Samuel”,
"EnglishName": “Samuel”,
},
{
"Parent_ID": 6,
"Name": “Badri”,
"EnglishName": “Badri”,
}]
},
{
"ID": 11788,
"MainServiceCategories_ID": 3,
"ServiceCategories_ID": 6,
"Children": [
{
"Parent_ID": 6,
"Name": “emma”,
"EnglishName": “Emma”,
},
{
"Parent_ID": 6,
"Name": “Sean”,
"EnglishName": “Sean”,
}]
}]
this is the code I am writing to do it
do{
let entityDic = responseDictionary["Entity"] as? [String: Any]
let servicesDic = entityDic?["Services"] as? [Any]
for i in 0 ..< servicesDic!.count {
let services = servicesDic?[i] as? [String: Any]
let availableServices = services!["EnglishName"]
servicesNamesArr.append(availableServices! as! String)
let children = services!["Children"]
childrenServices.append(children!)
let MainServiceCategories_ID = services!["MainServiceCategories_ID"]
MainServiceCategoriesID.append(MainServiceCategories_ID!)
}
print("childrenServices \(childrenServices)")
}
catch let error{
print(error)
}
I am able to get upto children dictionaries. Now the thing is I am trying to parse "English name" under children section and separate them based on "MainServiceCategories_ID" where I am getting fatal error in unwrapping the value.
for example if MainServiceCategories_ID =1 then that "English Name" need to be store in an array and if MainServiceCategories_ID =2 then into another array

The problem is, you are trying to parse "EnglishName" as a key of an element of the "Services" array and not as a key of a "Children" element. "Services" does not have a key "EnglishName", so you can't store that in servicesNamesArr. servicesDic should also be an Array of Dictionaries and not an array of Any objects.
See below code using guard statements using safe optional unwrapping:
do{
guard let entityDic = responseDictionary["Entity"] as? [String: Any] else {return}
guard let servicesDic = entityDic?["Services"] as? [[String:Any]] else {return}
for service in services {
guard let MainServiceCategoryID = service["MainServiceCategories_ID"] as? Int else {return}
MainServiceCategoriesID.append(MainServiceCategoryID)
guard let children = service["Children"] as? [[String:Any]] else {return}
childrenServices.append(children)
if MainServiceCategoryID == 3 {
for child in children {
if let availableService = child["EnglishName"] as? String {
servicesNamesArr.append(availableService)
}
}
}
}
print("childrenServices \(childrenServices)")
} catch let error{
print(error)
}

Related

How to parse local JSON data in Swift?

How to parse local JSON data where nested (optional) property is same as main.
Items data may be available or may not be available.
struct Category: Identifiable, Codable {
let id: Int
let name: String
let image: String
var items: [Category]?
}
I am using common Bundle extension to parse JSON data.
extension Bundle {
func decode<T: Codable>(_ file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}
For eg data :
[
{
"id": 1,
"name": "Apple",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone 11 Pro",
"image": "img_url"
},
{
"id": 2,
"name": "iPhone 11 Pro Max",
"image": "img_url"
}
]
},
{
"id": 2,
"name": "iPad",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPad mini",
"image": "img_url"
},
{
"id": 2,
"name": "iPad Air",
"image": "img_url"
},
{
"id": 3,
"name": "iPad Pro",
"image": "img_url"
}
]
}
]
},
{
"id": 2,
"name": "Samsung",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "Phone",
"image": "img_url"
},
{
"id": 2,
"name": "Tablet",
"image": "img_url"
}
]
}
]
Nesting is not the issue here, You are facing an Array of Contents. so you should pass [Content] to the decoder like:
let jsonDecoder = JSONDecoder()
try! jsonDecoder.decode([Category].self, from: json)
🎁 Property Wrapper
You can implement a simple property wrapper for loading and decoding all of your properties:
#propertyWrapper struct BundleFile<DataType: Decodable> {
let name: String
let type: String = "json"
let fileManager: FileManager = .default
let bundle: Bundle = .main
let decoder = JSONDecoder()
var wrappedValue: DataType {
guard let path = bundle.path(forResource: name, ofType: type) else { fatalError("Resource not found") }
guard let data = fileManager.contents(atPath: path) else { fatalError("File not loaded") }
return try! decoder.decode(DataType.self, from: data)
}
}
Now you can have any property that should be loaded from a file in a Bundle like:
#BundleFile(name: "MyFile")
var contents: [Content]
Note that since the property should be loaded from the bundle, I raised a FatalError. Because the only person should be responsible for these errors is the developer at the code time (not the run time).

Can't Decode With JSONDecoder

How can I decode this JSON by using JSONDecoder? I'm trying, but I always ended up crashing. I have another post talking about it but the error was bigger before, now I'm stucked only on that. I also tried by using JSONSerialization, but I think using JSONDecoder is more clean.
I got this error message when the compiler pass by JSONDecoder part:
Could not get API data. typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil)), The data couldn’t be read because it isn’t in the correct format.
This is how I'm trying to parse all my data:
import Foundation
//typealias AudiobookJSON = [[String: Any]]
struct APIClient {
static func getAudiobooksAPI(completion: #escaping ([Audiobook]?) -> Void) {
let url = URL(string: "https://alodjinha.herokuapp.com/categoria")
let session = URLSession.shared
guard let unwrappedURL = url else { print("Error unwrapping URL"); return }
let dataTask = session.dataTask(with: unwrappedURL) { (data, response, error) in
guard let unwrappedDAta = data else { print("Error unwrapping data"); return }
do {
let posts = try JSONDecoder().decode([Audiobook].self, from: unwrappedDAta)
print(posts)
completion(nil)
} catch {
print("Could not get API data. \(error), \(error.localizedDescription)")
}
}
dataTask.resume()
}
}
Struct that I'm using:
import Foundation
struct Data : Decodable {
let data : [Audiobook]
}
struct Audiobook: Decodable {
let id : Int?
let descricao : String?
let urlImagem : String?
// init(dictionary: Audiobook) {
// self.descricao = dictionary["descricao"] as! String
// self.urlImagem = dictionary["urlImagem"] as! String
//
// }
}
JSON to be parsed:
{
"data": [
{
"id": 1,
"descricao": "Games",
"urlImagem": "http:\/\/39ahd9aq5l9101brf3b8dq58.wpengine.netdna-cdn.com\/wp-content\/uploads\/2013\/06\/3D-Gaming.png"
},
{
"id": 2,
"descricao": "Livros",
"urlImagem": "http:\/\/4.bp.blogspot.com\/-6Bta1H9d22g\/UJAIJbqcHhI\/AAAAAAAAKi4\/hvgjWrlFc64\/s1600\/resenha-missiologia.png"
},
{
"id": 3,
"descricao": "Celulares",
"urlImagem": "http:\/\/pt.seaicons.com\/wp-content\/uploads\/2015\/11\/Mobile-Smartphone-icon.png"
},
{
"id": 4,
"descricao": "Inform\u00e1tica",
"urlImagem": "http:\/\/portal.ifrn.edu.br\/campus\/ceara-mirim\/noticias\/ifrn-oferece-curso-de-informatica-basica-para-pais-dos-estudantes\/image_preview"
},
{
"id": 5,
"descricao": "Eletrodom\u00e9stico",
"urlImagem": "http:\/\/classificados.folharegiao.com.br\/files\/classificados_categoria\/photo\/8\/sm_4d5ed3beb0f31b61cb9a01e46ecd0cf9.png"
},
{
"id": 6,
"descricao": "TVs",
"urlImagem": "http:\/\/i.utdstc.com\/icons\/256\/terrarium-tv-android.png"
},
{
"id": 7,
"descricao": "Filmes e S\u00e9ries",
"urlImagem": "https:\/\/pbs.twimg.com\/profile_images\/801033586438733824\/91Y_N91t_reasonably_small.jpg"
},
{
"id": 8,
"descricao": "M\u00f3veis e Decora\u00e7\u00f5es",
"urlImagem": "https:\/\/image.flaticon.com\/icons\/png\/128\/148\/148188.png"
},
{
"id": 9,
"descricao": "Moda, Beleza e Perfumaria",
"urlImagem": "http:\/\/icon-icons.com\/icons2\/196\/PNG\/128\/fashion_23852.png"
},
{
"id": 10,
"descricao": "Papelaria",
"urlImagem": "http:\/\/esen.pt\/in\/images\/stories\/skills_256.png"
}
]
}
You're trying to decode it as [AudioBook] when the data is actually a dictionary wrapping the array, as in your Data structure. Just change it to:
let data = try JSONDecoder().decode(Data.self, from: unwrappedDAta)
and you should be good to go.

Swift: NSDictionary as NSArray

let voicemailFiles = voicemail.value( forKey: "voicemail") as! [AnyObject]
// Could not cast value of type '__NSDictionaryI' (0x10ca8a228) to 'NSArray' (0x10ca89d78)
let voicemailFiles = voicemail.value( forKey: "voicemail") as! [String : String]
// Could not cast value of type '__NSArrayI' (0x104df9448) to 'NSDictionary' (0x104df7fa8).
When trying to put the JSON ( as seen below) into a variable, i am hitting the above errors for the 2 methods of casting for "voicemail".
let config = voicemail.value(forKey: "config") as AnyObject
This line for the key of config works perfectly.
The variable of voicemail is an [AnyObject] value of the key, voicemailboxes
{
"voicemailboxes": [
{
"config": {
"id": "5",
"description": "Test"
},
"voicemail": [
{
"id": "id001",
"caller": "...",
"caller_UK": "...",
"called": "+...",
"called_UK": "...",
"received": "...",
"duration_seconds": "..."
},
{
"id": "id002",
"caller": "...",
"caller_UK": "...",
"called": "..."
}
]
}
]
}
voicemailboxes is an array of Dictionary.
voicemail is also an array of Dictionary.
Need to parse them appropriately.
On playground:
let json = """
{
"voicemailboxes": [
{
"config": {
"id": "5",
"description": "Test"
},
"voicemail": [
{
"id": "id001",
"caller": "...",
"caller_UK": "...",
"called": "+...",
"called_UK": "...",
"received": "...",
"duration_seconds": "..."
},
{
"id": "id002",
"caller": "...",
"caller_UK": "...",
"called": "..."
}
]
}
]
}
"""
let data = json.data(using: .utf8)!
let jsonDict = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
let voicemailboxes = jsonDict["voicemailboxes"] as! [Any]
let voicemailboxesfirst = voicemailboxes.first as! [String:Any]
let voicemail = voicemailboxesfirst["voicemail"] as! [Any]
let voicemailfirst = voicemail.first as! [String:Any]
print(voicemailfirst)
Output:
["received": ..., "called": +..., "id": id001, "caller_UK": ..., "duration_seconds": ..., "caller": ..., "called_UK": ...]
As per your JSON it is clear that
voicemailboxes is [[string:Any]] (Array of Dictionary) and
voicemail is [[String:String]] (Array of Dictionary)
NOTE: guard or if let required to check correct response below is just example
You can access it like
((dictMain["voicemailboxes"] as! [[String:Any]]).first!["voicemail"] as! [[String:Any]])
Hope it is helpful to you
use below code
if let any = voicemail.value( forKey: "voicemail") {
if let tmpArray = any as? Array {
let voicemailFiles = tmpArray
}
}

Issue parsing JSON with Swift

I have a JSON file with this person objects. Each person has different information. This is the structure of the JSON file.
[
{
"person": {
"name": "Dani",
"job": "Artist",
"country": "FR",
"sold": "992",
"email": "Dani",
"facebook": "Artist",
"twitter": "Dani",
"instagram": "Artist",
"snapchat": "Dani",
"photo": "Artist"
}
},
{
"person": {
"name": "Alex",
"job": "",
"country": "TU",
"sold": "992",
"email": "Dani",
"facebook": "Artist",
"twitter": "Dani",
"instagram": "Artist",
"snapchat": "Dani",
"photo": "Artist"
}
}
]
I was able to open the json file but I am not able to parse it. This is my code
func lodData()
{
let data = NSData(contentsOfURL: url!)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if let person = json["person"] as? [[String: AnyObject]] {
for p in person {
if let name = p["name"] as? String {
names.append(name)
}
}
}
} catch {
print("error serializing JSON: \(error)")
}
print(names)
}
As result the names array still empty.
person is [String: String] means dictionary not an array .. .you can do something like this
if let data = json as? [[String: AnyObject]] {
for p in data {
if let person = p["person"] as? [String: String]{
names.append(person["name"])
}
}
}

How to get specific value from JSON in swift

I can successfully parse all data from my json file. In my application in the collection view i try to parse only 1 image data to cell but i got related all data with it. I'll share the JSON code and the parse code with you and lastly screen shot of simulator. I hope you can help me about it.
Thank you,
JSON code
{
"retcode": "200",
"content": [{
"id": 3,
"name": "X Treme",
"desc": "Polikarbon G\u00f6vde",
"category": "Design",
"thumbnail": [{
"id": 2,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_red.jpg"
}, {
"id": 3,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_orange.jpg"
}, {
"id": 4,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_blue.jpg"
}, {
"id": 5,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_green.jpg"
}, {
"id": 6,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_clear.jpg"
}, {
"id": 7,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_grey.jpg"
}, {
"id": 8,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_slred.jpg"
}]
}, {
"id": 4,
"name": "Opal",
"desc": "Polikarbon Sandalye",
"category": "Design",
"thumbnail": [{
"id": 9,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/opal_orange.jpg"
}, {
"id": 10,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/opal_blue.jpg"
}, {
"id": 11,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/opal_green.jpg"
}]
}],
"error_msg": ""
}
Swift Code
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: [])
{
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
var valueforname:Int = 0
if let blogs = json["content"] as? [[String: AnyObject]] {
for blog in blogs {
if let blog2 = blog["thumbnail"] as? [[String: AnyObject]] {
for blog3 in blog2 {
if let blog4 = blog3["thumbnail"] as? String {
var checkvalue1 = blog3["id"] as? Int
if Imagearray.contains(String(checkvalue1!)) {
}
else {
Imagearray.append(blog4)
}
}
}
}
}
}
}
catch {
print("error serializing JSON: \(error)")
}
}
}
and here is the screen shots. You can see the different colors of chairs I want to only 1 color for each item
Try this one:
NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: [])
{
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
var valueforname:Int = 0
if let blogs = json["content"] as? [[String: AnyObject]] {
for blog in blogs {
if let blog2 = blog["thumbnail"] as? [[String: AnyObject]] {
if let blog4 = blog2["thumbnail"] as? String
{
Imagearray.append(blog4)
}
}
}
}
}
catch {
print("error serializing JSON: \(error)")
}
}
}