Reading and parsing complex JSON - Swift - ios

I've been trying to read this complex JSON Data in Swift, which i can read the "feed" and then "entry" until reaching "url" for each element in the JSON.
This is the JSON (Dictionary - Array - Dictionary - Array - Dictionary ):
{
"feed": {
"entry": [
{
"media$group": {
"media$content": [
{
"url": "http://..../photo.png"
}
]
}
}
]
}
}
This is my Photo Class where i define the things i need to get from JSON:
class Photo {
let TAG_FEED:String = "feed"
let TAG_ENTRY:String = "entry"
let TAG_MEDIA_GROUP:String = "media$group"
let TAG_MEDIA_CONTENT:String = "media$content"
let TAG_IMG_URL:String = "url"
}
This is my UIViewController where I'm trying to read the JSON :
func parseJsonData(data: NSData) -> [Photo] {
var photos = [Photo]()
var error:NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
if error != nil {
println(error?.localizedDescription)
}
// how is it possible to read it here
return photos
}
After reading the array is it possible to store it array of Strings.

If you want to parse then use below piece of code and learn.
if let myFeed = jsonResult[TAG_FEED] as? Dictionary<String, AnyObject> {
if let myEntries = myFeed[TAG_ENTRY] as? Array<AnyObject> {
for myEntry in myEntries {
if let myMedia = myEntry[TAG_MEDIA_GROUP] as? Dictionary<String, AnyObject> {
if let contents = myMedia[TAG_MEDIA_CONTENT] as? Array<AnyObject> {
for myContent in contents {
if let url = myContent[TAG_IMG_URL] as? String {
// populate your model class here...
}
}
}
}
}
}
}
Change your jsonResult line to let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableLeaves, error: &error) as! Dictionary<String, AnyObject>!

As alternative you can use SwiftyJSON

Related

How to Get JSON array which will be later stored in Realm table?

This is part of JSON my API returns, I don't know how to get that and store in Realm table?
"images": {
"image1": {
"url": "http://www.example.com/900150983cd24fb0d6963f7d28e17f72.gif",
"path": "data/2clb91218/900150983aassfb0d6963f7d28e17f72.gif",
"alt": "YWJj"
},
"image2": {
"url": "http://example/tex/fbce4a1ebe576539394e9493e30c7e5e.gif",
"path": "data/2clb91218/900150983cd24fb0d6963f7d28e332g22.gif",
"alt": "Yaaase22"
},
"image3": {
"url": "https://example.amazonaws.com/card/480_300/9/9jpul1218.jpg",
"path": "data/2clb91218/ewfregwrgw6963f7d28e17f72.gif",
"alt": "pic3"
}
}
NOTE : Dont test these urls, I didnt want to put real ones.
NOTE : In object 'Images', child-object's names are chaning so we have: 'image1' , 'image2' etc.
I have read on Stackoverflow that for storing arrays in RealmTable, we need to use non-dynamic List<> , so this is how I store image-urls in class:
var images = List<String>()
This is part of my responseJSON function where I am trying to Get images from API, 'continue' is there just to Get another objects if images are unable to Get :
guard let images = data["images"] as? [String: Any?],
let image = images["image1"] as? [String: String],
let imageUrl = image["url"] as? String
else { print("error") ; continue }
This above works for 'image1', but what about other image names, how to handle them?
And one more problem - 'Images' object can return NIL if there are no images which can aslo happen. If it's nil, I want to put "null" in RealmTable's column 'images' of the type I mentioned above.
I hope you can fully understand me, if not, comment and I will try to explain better.
You need
guard let images = data["images"] as? [String:[String:Any]] else { return }
let res = Array(images.values)
let urls = res.compactMap { $0["url"] as? String }
print("All urls : ",urls)
var images = List<String>()
let imagesDictInfo = responseData.toDictionary()
if let imageArray = imagesDictInfo["Images"] as? [[String:Any]]{
let imageArrayData = image.getJson()
//Store this ImageArray into realm or
for image in imageArray{
let imageData = image.getJson()
//Store Image Data
images.append(imageData)
}
}
func getJson(_ jsonString:String) -> [String:Any]?{
do{
let data = jsonString.data(using: .utf8)
if let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any] {
return json
}else{
return nil
}
}catch{
return nil
}
}
func toDictionary() -> NSDictionary {
let blankDict : NSDictionary = [:]
if let data = self.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
} catch {
print(error.localizedDescription)
}
}
return blankDict
}

Swift Compact Map returning empty

Hi I am trying to learn RXSwift and First time I came across these concepts like Maps and Compact Maps.
I am able to get the response, but this line always returns empty.
objects.compactMap(DummyUser.init)
fileprivate let Users = Variable<[DummyUser]>([])
fileprivate let bag = DisposeBag()
response
.filter { response, _ in
return 200..<300 ~= response.statusCode
}
.map { _, data -> [[String: Any]] in
guard (try? JSONSerialization.jsonObject(with: data, options: [])) != nil else {
return []
}
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
// print(json!["results"])
return json!["results"] as! [[String : Any]]
}
.filter { objects in
return objects.count > 0
}
.map { objects in
// objects.forEach{print($0["name"]!)}
let names = objects.map { $0["name"]!}
print(names)
return objects.compactMap(DummyUser.init)
}
.subscribe(onNext: { [weak self] newEvents in
self?.processEvents(newEvents)
})
.disposed(by: bag)
func processEvents(_ newEvents: [DummyUser]) {
var updatedEvents = newEvents + Users.value
if updatedEvents.count > 50 {
updatedEvents = Array<DummyUser>(updatedEvents.prefix(upTo: 50))
}
Users.value = updatedEvents
DispatchQueue.main.async {
self.MianUsertable.reloadData()
}
// refreshControl?.endRefreshing()
let eventsArray = updatedEvents.map{ $0.dictionary } as NSArray
eventsArray.write(to: userFileURL, atomically: true)
}
My Json Response is Here
https://randomuser.me/api/?results=5
DummyUser Class
import Foundation
typealias AnyDict = [String: Any]
class DummyUser {
let gender: String
let name: AnyDict
let dob: String
let picture: AnyDict
init?(dictionary: AnyDict) {
guard let Dgender = dictionary["gender"] as? String,
let Dname = dictionary["name"] as? AnyDict,
let birthdata = dictionary["dob"] as? AnyDict,
let Ddob = birthdata["dob"] as? String,
let Dpicture = dictionary["picture"] as? AnyDict
else {
return nil
}
gender = Dgender
name = Dname
dob = Ddob
picture = Dpicture
}
var dictionary: AnyDict {
return [
"user": ["name" : name, "gender": gender, "dob": dob],
"picture" : ["userImage": picture]
]
}
}
In your DummyUser model you are using failable initializer, so in case of wrong dictionary provided to init method it will return nil.
compactMap automatically automatically filters nil's and that's the reason why your output is empty.
Looking at this piece of code:
let names = objects.map { $0["name"]!}
return objects.compactMap(DummyUser.init)
I would debug this variable called names because it probably has wrong input for the DummyUser initializer. It should be dictionary containing all of your DummyUser parameters. You can also debug your failable initializer to see which of the parameter is missing.

Convert JSON nested objects

I am getting the following JSON from Foursquare API and I have been struggling with extracting the data:
{
"meta":{
"code":200,
"requestId":"58122e59498e5506a1b23580"
},
"response":{
"venues":[
{
"id":"4d56c381a747b60cd4a12c2b",
"name":"Sports Circle",
"contact":{},
"location":{
"lat":31.9,
"lng":35.9,
"labeledLatLngs":[
{
"label":"display",
"lat":31.9,
"lng":35.90
}
],
],
"confident":true
}
}
}
I want to get the name in venues in addition to the lat and lng values. I have tried this so far but it gets out of the second if statement at JVenues because it is nil:
func parseData (JSONData: Data){
do {
var readableJson = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! [String:AnyObject]
if let JResponse = readableJson ["response"] as? [String:AnyObject] {
if let JVenues = JResponse["venues"] as? [String:AnyObject]{
if let JName = JVenues["name"] as? String{
NSLog(JName)
}
}
}
} catch {
print(error)
}
}
This is what the other answers are getting at. Will probably make more sense if you can see it all laid out...
if let JResponse = readableJson ["response"] as? [String : AnyObject] {
if let JVenues = JResponse["venues"] as? [[String : AnyObject]] {
if let JName = JVenues.first?["name"] as? String {
NSLog(JName)
}
}
}
Note this only gets the FIRST name in the array of venues.
EDIT:
I prefer something like this. Define a struct and convert your dictionaries to the struct:
struct Venue {
var name: String?
var venueId: String?
init(_ venueDictionary: [String : AnyObject]) {
self.name = venueDictionary["name"] as? String
self.venueId = venueDictionary["id"] as? String
}
}
In your class create a property such as:
var venues = [Venue]()
From your JSON map the dictionaries to the venue array. I renamed variables that start with a capital for convention.
if let response = readableJson ["response"] as? [String : AnyObject] {
if let responseVenues = response["venues"] as? [[String : AnyObject]] {
self.venues = responseVenues.map({ Venue($0)) })
}
}
Use anywhere in your class like:
let venue = self.venues.first
print(venue?.name)
Or:
if let venue = self.venues.find({ $0.name == "Sports Circle" }) {
print("found venue with id \(venue.venueId)")
}

Retrieve specific Array from JSON in swift in the form of Array of Keys and Values?

JSON :
{
"11/08/22":[
{
"Bill Gates":"Microsoft",
"Steve Balmer":"Microsoft"
}],
"13/08/22":[
{
"Tim Cook":"Apple",
"Jony Ive":"Apple"
}]
}
Swift Code :
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
print(jsonResult)
//Get Result into Seperate Arrays
let keys = jsonResult.flatMap(){ $0.0 as? String }
let values = jsonResult.flatMap(){ $0.1 as? String }
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
Requirements :
If i pass from program "11/08/22", I should be able to get all keys and values in the form of Array of String of only that array named "11/08/22" .
Better Explanation :
It should go into 11/08/22 and it should retrieve "Bill Gates","Steve Balmer" as Keys and "Microsoft" As a value in two separate arrays
For this example let's use an array to collect the people and a set to collect the companies:
var people: [String] = []
var companies: Set<String> = []
Subscript to the JSON dictionary with your "11/08/22" key and cast the result as an array of dictionaries.
Loop over this array and in the loop, add the keys to the people array and insert the values in the companies set.
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
if let dateContent = jsonResult["11/08/22"] as? [[String:String]] {
for group in dateContent {
people.appendContentsOf(group.keys)
group.values.forEach { companies.insert($0) }
}
}
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
print(people)
print(companies)
Result:
["Steve Balmer", "Bill Gates"]
["Microsoft"]
let keys=jsonResult["11/08/22"]?.allKeys as? [String];
let values=jsonResult["11/08/22"]?.allValues as? [String];
It was as simple as this

Json parsing in iOS playground do method is not parsing

I am trying to parse some json data into three different arrays based off the label in the json. I seem to be stuck and don't know why my for loop is never being entered. I am new to iOS and am using this to learn swift. Any help will be appreciated.
Here is the code that I am using:
var myPicture = [String]()
var myPath = [String]()
var mylabel = [String]()
let jsonString = "[{\"picture\" : \"Picture 1 \", \"path\": \"Path 1\" , \"label\" : \"Label 1\"}]"
//Convert jsonString to NSData
let myData = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
do{
let promoJson = try NSJSONSerialization.JSONObjectWithData(myData, options:.AllowFragments)
if let promtions = promoJson[""] as? [[String: AnyObject]] {
for promtions in promtions {
if let picture = promtions["picture"] as? String
{
myPicture.append(picture)
if let path = promtions["path"] as? String
{
myPath.append(path)
if let label = promtions["label"] as? String
{
mylabel.append(label)
}
}
}
}
}
}catch {
print("Error with Json: \(error)")
}
print(myPicture.first)
print(myPath.first)
print(mylabel.first)
The results for the print are all nil. So nothing is being appended to the arrays
The if let promtions = promoJson[""] part won't work and would be useless anyway. This is only promoJson that you have to cast to an array of dictionaries.
You weren't that far from the solution, look at my working version of your code:
do {
let promoJson = try NSJSONSerialization.JSONObjectWithData(myData, options: [])
if let promtions = promoJson as? [[String: AnyObject]] {
for promtion in promtions {
if let picture = promtion["picture"] as? String {
myPicture.append(picture)
}
if let path = promtion["path"] as? String {
myPath.append(path)
}
if let label = promtion["label"] as? String {
mylabel.append(label)
}
}
}
} catch let error as NSError {
print(error.debugDescription)
}
Alternative
Now that the issue is resolved, let me suggest you another way: instead of separate arrays for your data, use one array of objects holding your data.
For example, make a struct like this:
struct Promotion {
let picture: String
let path: String
let label: String
}
And an array for instances of this struct:
var myPromotions = [Promotion]()
Now we can decode the JSON, create objects from it then store them in the array:
do {
let promoJson = try NSJSONSerialization.JSONObjectWithData(myData, options: [])
if let promtions = promoJson as? [[String: AnyObject]] {
for promtion in promtions {
if let picture = promtion["picture"] as? String,
path = promtion["path"] as? String,
label = promtion["label"] as? String {
let promo = Promotion(picture: picture, path: path, label: label)
myPromotions.append(promo)
}
}
}
} catch let error as NSError {
print(error.debugDescription)
}
Now look at the content of the array, very convenient:
for promo in myPromotions {
print(promo.label)
print(promo.path)
print(promo.picture)
}
When you are converting it is already an array.
import Foundation
import UIKit
var myPicture = [String]()
var myPath = [String]()
var mylabel = [String]()
let jsonString = "[{\"picture\" : \"Picture 1 \", \"path\": \"Path 1\" , \"label\" : \"Label 1\"}]"
//Convert jsonString to NSData
let myData = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
do{
let promoJson = try NSJSONSerialization.JSONObjectWithData(myData, options:.AllowFragments) as! NSArray
for promtions in promoJson {
if let picture = promtions["picture"] as? String
{
myPicture.append(picture)
if let path = promtions["path"] as? String
{
myPath.append(path)
if let label = promtions["label"] as? String
{
mylabel.append(label)
}
}
}
}
}catch
{
print("Error with Json: \(error)")
}
print(myPicture.first) // "Optional("Picture 1 ")\n"
print(myPath.first) // "Optional("Path 1")\n"
print(mylabel.first) // "Optional("Label 1")\n"
This does the job.

Resources