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"])
}
}
}
Related
I am new to swift . I created simple playground and added the file with extension json into playground . I am trying to decode the result and print the ID into console but any reason ,it not printing the result into console , I do not see error into console window ..
Here is the playground project structure ..
Here is my json file ..
let json = """
{
"id": "1",
"options": [
{
"id": "11",
"options": [
{
"id": "111",
"options": []
}
]
},
{
"id": "2",
"options": [
{
"id": "21",
"options": []
},
{
"id": "22",
"options": [
{
"id": "221",
"options": []
}
]
}
]
}
]
}
Here is the code .. I tried ..
struct People: Codable {
let id: String
let options: [People]
}
func loadJson(filename fileName: String) -> People? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(People.self, from: data)
print(jsonData.id)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
It not printing the ID of the decode json ..
So I did get it to print the ID, I changed the people file name to people.json and changed the contents to:
{
"id": "1",
"options": [{
"id": "11",
"options": [{
"id": "111",
"options": []
}]
},
{
"id": "2",
"options": [{
"id": "21",
"options": []
},
{
"id": "22",
"options": [{
"id": "221",
"options": []
}]
}
]
}
]
}
(Notice I removed the let json = statement.)
After that in the playground where define the People struct and the loadJson method you can call it like so:
loadJson(filename: "people")
So now you end up with:
struct People: Codable {
let id: String
let options: [People]
}
func loadJson(filename fileName: String) -> People? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(People.self, from: data)
print(jsonData.id)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
loadJson(filename: "people")
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).
I have a local JSON and try to decode but got "Expected to decode Array but found a dictionary instead" error. The json file and two structs below:
{
"Stanford University": [{
"type": "government",
"name": "Stanford University",
"city": "Santa Clara",
"major": "Computer Engineering"
},
{
"type": "government",
"name": "Stanford University",
"city": "Santa Clara",
"major": "Economics"
}
],
"Berkeley University": [{
"type": "foundation",
"name": "Berkeley University",
"city": "Alameda",
"major": "Communication"
},
{
"type": "foundation",
"name": "Berkeley University",
"city": "Alameda",
"major": "Physics"
}
]
}
two structs:
struct Universite4: Codable {
let name: String?
let major:[Major]?
}
struct Major: Codable {
let type: String?
let name: String?
let major: String? }
And this is code for data load and decode;
public class DataLoader {
#Published var universite4 = [Universite4]()
init() {
load()
}
func load() {
if let unv4json = Bundle.main.url(forResource: "unv4", withExtension: "json") {
do {
let data = try Data(contentsOf: unv4json)
let jsonDecoder = JSONDecoder()
let dataFromJson = try jsonDecoder.decode([Universite4].self, from:data)
self.universite4 = dataFromJson
} catch {
print("Error: \(error)")
}
}
}
}
Does anybody know how can I fix above code? Regards.
Try to change, the issue here is that actually your keys are sort of "Dynamic keys" which I don't recommend but if you have to use them, so try this.
let dataFromJson = try jsonDecoder.decode([Universite4].self, from:data)
to
let dataFromJson = try jsonDecoder.decode([String:[Major]].self, from:data)
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)
}
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)")
}
}
}