create a dictionary from json data values - ios

I'm trying to create a dictionary from 2 json values, so far I can get one and append to an array but instead I would like to combine 2 values as a dictionary and add them to an array. This is what my function currently looks like:
var menuButtonsArray: [String?] = []
func getAppMenuButtons() {
guard let appJSON = appMenuJSON else {return}
guard let items = appJSON["items"] as? [[String: Any]] else {
return
}
let filteredItem = items.filter{$0["itemId"] as! String == "Items"}[0]
if let data = filteredItem["data"] as? [[String:Any]] {
for embeddedDict in data {
for (key, value) in embeddedDict {
if key == "name" {
menuButtonsArray.append(value as! String)
print("key: \(value)")
}
}
}
}
}
This is what the json looks like, it's shortened to fit this post:
"itemId": "Items",
"data": [
{
"name": "Item1",
"url": "www.item1.com"
},
{
"name": "Item2",
"url": "www.item2.com"
},
{
"name": "Item2",
"url": "www.item2.com"
}
]
What I'm trying to do is create an array of dictionaries with the format:
name:url
so something like :
["item1":"www.item1.com","item2":"www.item2.com", "item3":"www.item3.com"]

Here is an example on how you can use map() get the array you want:
var data = [["name": "Item1","url": "www.item1.com"],
["name": "Item2","url": "www.item2.com"],
["name": "Item3","url": "www.item3.com"]]
var newData = data.map { (dict: Dictionary) -> Dictionary<String, String> in
return [dict["name"]!:dict["url"]!] }
This code should give you
[ ["Item1": "www.item1.com"], ["Item2": "www.item2.com"], ["Item3":
"www.item3.com"] ]
as output

First of all if you need a Dictionary then declare your result as Dictionary instead of Array, you can use subscribe to use your embeddedDict["name"] as key in your resultDictionary and your embeddedDict["url"] as your resultDictionary value
func getAppMenuButtons() {
guard let appJSON = appMenuJSON else {return}
guard let items = appJSON["items"] as? [[String: Any]] else {
return
}
let filteredItem = items.filter{$0["itemId"] as! String == "Items"}[0]
var resultDict : [String:String] = [:]
if let data = filteredItem["data"] as? [[String:Any]] {
guard embeddedDict["name"] != nil && embeddedDict["url"] else{
continue
}
for embeddedDict in data {
resultDict[embeddedDict["name"]] = embeddedDict["url"]
}
}
debugPrint(resultDict) //in resultDict you will have your desired result
}

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.

Fetching every object in nested JSON

So here you can see my method which fetches JSON. My problem is that I want my loop to go through every object. Not one object 10 times like it is now. I know that player["1"] is causing it to fetch first object repeatedly, it is just for the sake of the example. I need to get every player information. Could someone please fix this logic and little bit explain the situation.
var homeTeamPlayers: [MatchUp]? = []
let urlRequest = URLRequest(url: URL(string: "http://www.fibalivestats.com/data/586746/data.json")!)
func fetchHomeTeam() {
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error!)
return
}
homeTeamPlayers = [MatchUp]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let teamsFromJSON = json["tm"] as? [String : AnyObject] {
if let homeTeam = teamsFromJSON["1"] as? [String : AnyObject] {
if let player = homeTeam["pl"] as? [String : AnyObject] {
for _ in player {
let homeTeamPlayer = MatchUp()
if let firstPlayer = player["1"] as? [String : AnyObject] {
if let name = firstPlayer["name"] as? String {
homeTeamPlayer.homeTeam_name = name
}
}
homeTeamPlayers?.append(homeTeamPlayer)
}
}
}
}
} catch let error {
print(error)
}
}
task.resume()
}
Here is the JSON I would like to fetch...
{
tm: {
1: {
pl: {
1: {
name: "R. Miniotas"
},
2: {
name: "T. Delininkaitis"
},
3: {
name: "V. Cizauskas"
},
4: {
name: "T. Klimavicius"
},
5: {
name: "V. Lipkevicius"
},
6: {
name: "M. LinkeviÄius"
},
7: {
name: "D. Seskus"
},
8: {
name: "T. Michnevicius"
},
9: {
name: "D. Gvezdauskas"
},
11: {
name: "A. Valeika"
}
}
You need to enumerate the dictionary using for (key, value) in syntax:
if let players = homeTeam["pl"] as? [String : Any] {
for (_, value) in players {
let homeTeamPlayer = MatchUp()
if let currentPlayer = value as? [String : Any],
let name = currentPlayer["name"] as? String {
homeTeamPlayer.homeTeam_name = name
}
homeTeamPlayers?.append(homeTeamPlayer)
}
}
However to avoid empty Matchup instances I'd recommend
if let players = homeTeam["pl"] as? [String : Any] {
for (_, value) in players {
if let currentPlayer = value as? [String : Any],
let name = currentPlayer["name"] as? String {
let homeTeamPlayer = MatchUp()
homeTeamPlayer.homeTeam_name = name
homeTeamPlayers?.append(homeTeamPlayer)
}
}
}
Note: The JSON dictionary in Swift 3 is [String:Any] and why is the homeTeamPlayers array optional?
And finally – as always - .mutableContainers is completely meaningless in Swift.
Your JSON data is a dictionary of dictionaries of dictionaries.
It looks like there is an outer key "tm" (teams) that then contains a dictionary with keys "1", "2", etc for each team, and then inside the teams, there are more dictionaries that again use the keys "1", "2", "3", etc for the players. This is not a great structure.
If you don't care about the order in which you fetch the items from your dictionaries then you can use the syntax:
for (key, value) in dictionary
...to loop through all the key/value pairs in a dictionary, but you do need to be aware that the order you get the key/value pairs is not defined. It might give you the entries in key order sometimes, and not in order other times.
If order is important then you would need to fetch the keys first, sort them, and then fetch the items (or some other technique). Getting the values sorted by key might look like this:
let keys = dictionary.keys.sorted($0 < $1)
for aKey in keys {
let aValue = dictionary[aKey]
//Do whatever you need to do with this entry
}
EDIT:
Your JSON data needed some editing to make it legal JSON:
{
"tm" : {
"1" : {
"pl" : {
"7" : {
"name" : "D. Seskus"
},
"3" : {
"name" : "V. Cizauskas"
},
"8" : {
"name" : "T. Michnevicius"
},
"4" : {
"name" : "T. Klimavicius"
},
"11" : {
"name" : "A. Valeika"
},
"9" : {
"name" : "D. Gvezdauskas"
},
"5" : {
"name" : "V. Lipkevicius"
},
"1" : {
"name" : "R. Miniotas"
},
"6" : {
"name" : "M. LinkeviÃÂius"
},
"2" : {
"name" : "T. Delininkaitis"
}
}
}
}
}
(All the keys and values had to be enclosed in quotes, and I needed to add closing braces to terminate the object graph)
I wrote code that took the above and read it into a JSON object, and then wrote it back out to JSON without whitespace. I then converted all the quotes to \".
Here is tested code that parses the JSON into objects, and then walks your data structure, with quite a bit of error checking:
let jsonString = "{\"tm\":{\"1\":{\"pl\":{\"7\":{\"name\":\"D. Seskus\"},\"3\":{\"name\":\"V. Cizauskas\"},\"8\":{\"name\":\"T. Michnevicius\"},\"4\":{\"name\":\"T. Klimavicius\"},\"11\":{\"name\":\"A. Valeika\"},\"9\":{\"name\":\"D. Gvezdauskas\"},\"5\":{\"name\":\"V. Lipkevicius\"},\"1\":{\"name\":\"R. Miniotas\"},\"6\":{\"name\":\"M. LinkeviÃius\"},\"2\":{\"name\":\"T. Delininkaitis\"}}}}}"
guard let data = jsonString.data(using: .utf8) else {
fatalError("Unable to convert string to Data")
}
var jsonObjectOptional: Any? = nil
do {
jsonObjectOptional = try JSONSerialization.jsonObject(with: data, options: [])
} catch {
print("reading JSON failed with error \(error)")
}
guard let jsonObject = jsonObjectOptional as? [String: Any] else {
fatalError("Unable to read JSON data")
}
//Fetch the value for the "tm" key in the outer dictionary
guard let teamsFromJSON = jsonObject["tm"] as? [String : Any] else {
fatalError("Can't fetch dictionary from JSON[\"tm\"]")
}
let teamKeys = teamsFromJSON
.keys //Fetch all the keys from the teamsFromJSON dictionary
.sorted{$0.compare($1, options: .numeric) == .orderedAscending} //Sort the key strings in numeric order
print("teamsFromJSON = \(teamsFromJSON)")
//loop through the (sorted) team keys in teamKeys
for aTeamKey in teamKeys {
guard let aTeam = teamsFromJSON[aTeamKey] as? [String: Any] else {
print("Unable to read array of teams")
continue
}
//Fetch the dictionary of players for this team
guard let playersDict = aTeam["pl"] as? [String: Any] else {
print("Unable to read players dictionary from team \(aTeamKey)")
continue
}
//Fetch a sorted list of player keys
let playerKeys = playersDict
.keys
.sorted{$0.compare($1, options: .numeric) == .orderedAscending}
print("playerKeys = \(playerKeys)")
//Loop through the sorted array of player keys
for aPlayerKey in playerKeys {
//Fetch the value for this player key
guard let aPlayer = playersDict[aPlayerKey] as? [String: String] else {
print("Unable to cast aPlayer to type [String: String]")
continue
}
//Attempt to read the "name" entry for this player.
guard let playerName = aPlayer["name"] else {
continue
}
//Deal with a player in this team
print("In team \(aTeamKey), player \(aPlayerKey) name = \(playerName)")
}
}
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject, options: []),
let jsonString = String(data: jsonData, encoding: .ascii) {
print("\n\njsonString = \n\(jsonString)")
}
The output of that code is:
playerKeys = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "11"]
In team 1, player 1 name = R. Miniotas
In team 1, player 2 name = T. Delininkaitis
In team 1, player 3 name = V. Cizauskas
In team 1, player 4 name = T. Klimavicius
In team 1, player 5 name = V. Lipkevicius
In team 1, player 6 name = M. LinkeviÃius
In team 1, player 7 name = D. Seskus
In team 1, player 8 name = T. Michnevicius
In team 1, player 9 name = D. Gvezdauskas
In team 1, player 11 name = A. Valeika
(Your data is missing an entry for player 10.)
Suppose this is my URL:
URL: https://maps.googleapis.com/maps/api/directions/json?origin=19.0176147,72.8561644&destination=26.98228,75.77469&sensor=false
The URL mentioned above when entered in browser gives me a JSON response from google.
In this response, I have to fetch the value of distance and duration.
So my swift code to fetch the value is:
do{
let data = try Data(contentsOf: url!)
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
if let routes = json?["routes"] as AnyObject? as? [[String: AnyObject]] {
//print(routes)
for r in routes {
if let legs = r["legs"] as? [[String: AnyObject]] {
for l in legs {
//Start Address
print("Start Address: \((l["start_address"]!) as Any)")
// End Address
print("End Address: \((l["end_address"]!) as Any)")
//Distance
if let distance = l["distance"] as? [String: AnyObject] {
distanceResult = (distance["text"]!) as Any as? String
print("Distance: \(distanceResult!)")
}
// Duration
if let duration = l["duration"] as? [String: AnyObject] {
durationResult = (duration["text"]!) as Any as? String
print("Duration: \(durationResult!)")
}
}
googleResult = distanceResult+durationResult
}
}
}
}
catch
{
distanceResult = ""
durationResult = ""
print("error in JSONSerialization")
}
if(googleResult != ""){
googleResult = "Distance: \(distanceResult!), Duration: \(durationResult!)"
}
else{
print("googleResult is nil")
distanceResult = ""
durationResult = ""
}
return googleResult

Swift 3 - save json using struct model with null value

I am working on consuming the restful web service which returns the data in JSON format. This is the Sample data :
[
{
"name": "Mark E",
"categories": "process",
"id": 1,
"checkedOut": null,
"checkedOutBy": null
},
{
"name": "John",
"categories": null,
"id": 2,
"checkedOut": null,
"checkedOutBy": null
}
]
I am parsing this json using this code. Also I created struct model for this.
let task = session.dataTask(with: url) { (data, response, error) in
var myModel = [MyModel]()
if let HTTPResponse = response as? HTTPURLResponse {
let status = HTTPResponse.statusCode
if(status == 200) {
guard let data = data else {
print("No Data!!")
return completion(false, myModel)
}
guard let json = try! JSONSerialization.jsonObject(with: data, options: []) as? NSArray else {
print("Not an array")
return completion(false, myModel)
}
for jsondata in json {
guard let newdata = MyModel(json: jsondata) else {
continue
}
myModel.append(newdata)
}
completion(true,myModel)
}else {
completion(false,myModel)
}
}
}
task.resume()
And this is my data model struct
struct MyModel {
var name : String
var categories : String
var id : Int
var checkedOut : String
var checkedOutBy : String
init?(json:Any) {
guard let myModel = json as? NSDictionary else {
return nil
}
guard let name = myModel["name"] as? String,
let id = myModel["id"] as? Int else {
return nil
}
self.name = author
self.id = id
// This is how I am handling the null values.
if let categories = myModel["categories"] as? String {
self.categories = categories
} else {
self.categories = ""
}
if let lastCheckedOut = myModel["lastCheckedOut"] as? String {
self.lastCheckedOut = lastCheckedOut
} else {
self.lastCheckedOut = ""
}
if let lastCheckedOutBy = myModel["lastCheckedOutBy"] as? String {
self.lastCheckedOutBy = lastCheckedOutBy
}else {
self.lastCheckedOutBy = ""
}
}
}
Here in struct MyModel, I am using if let for checking null values. Could anyone please suggest me this is the right way to check each variable for null? Is there any other way to do this?
If I check for null value using guard, its not adding any object into array.
If the value is null then it simply assign empty char "" to that variable and it will add into MyModel array of objects.
The usual way to check for a specific type and assign something else on failure is the nil coalescing operator:
self.lastCheckedOut = myModel["lastCheckedOut"] as? String ?? ""
Side-note: Consider that JSON null is deserialized to NSNull rather than to nil
I would advise on a couple of approaches:
Setting MyModel struct properties as optionals. In this way, you don't need to use if let or guard statements.
I can recommend ObjectMapper, a Swift 3 pods which maps JSON very efficiently. For example:
Instead of assigning:
If let _value = dictionary["value"] as? String { self.value = _value
}
You will use
self.value = map["value"]
ObjectMapper
Wrote this on my phone, untested ;-)...

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)")
}

Resources