filtering JSON data in swift - json

I am trying to parse my JSON data and append only those objects into array which meets specified condition. At the moment I have commented out code which fetch all objects from the API and add them into array. However, I would like to limit it so that it only appends objects with "wger.de" value for "license_author" key.
However I am getting error on line:
if eachExercise["license_author"] == "wger.de"
Binary operator '==' cannot be applied to operands of type 'Any?' and 'String'.
However I still wants to keep it as Any object because I would like to fetch both strings and integers data from my API.
This is the code for my parseData() function:
func parseData() {
fetchedExercise = []
let urlPath = "https://wger.de/api/v2/exercise/?format=json&language=2&status=2"
let url = URL(string: urlPath)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error while parsing JSON")
}
else {
do {
if let data = data,
let fetchedData = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String:Any],
let exercises = fetchedData["results"] as? [[String: Any]] {
// WORKING CODE
/*
for eachExercise in exercises
{
let name = eachExercise["name"] as! String
let description = eachExercise["description"] as! String
self.fetchedExercise.append(Exercise(name: name, description: description))
}
*/
// TESTING
for eachExercise in exercises {
if eachExercise["license_author"] == "wger.de" {
let name = eachExercise["name"] as! String
let description = eachExercise["description"] as! String
let id = eachExercise["id"] as! Int
self.fetchedExercise.append(Exercise(name: name, description: description))
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
catch {
print("Error while parsing data.")
}
}
}
task.resume()
}

Use the where clause and optional downcast Any to String
for eachExercise in exercises where eachExercise["license_author"] as? String == "wger.de" { ...

You need cast it to String.
if eachExercise["license_author"] as? String == "wger.de" {
}

Related

parsing json array in label swift 4

This is my JSON array
[
{
"dollar":"15000",
"date":"1397-12-12"
}
]
i want to show dollar and date values in two different label but i have this type mismatch 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))
this is my Struct :
struct Dollar_Date : Codable {
let dollar : String
let date : String
}
and this is my code in view controller :
func dollarLabel() {
let urlString = DOLLAR_DATE_BASE_URL
guard let url = URL(string: urlString) else { return }
// 2
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
do {
// 3
//Decode data
let JSONData = try JSONDecoder().decode(Dollar_Date.self, from: data)
// 4
print(JSONData.dollar)
//Get back to the main queue
DispatchQueue.main.async {
self.main_Price.text = JSONData.dollar
self.data_Label.text = JSONData.date
}
} catch let jsonError {
print(jsonError)
}
// 5
}.resume()
}
Try this:
guard let jsonArray = JSONData as? [[String: Any]] else {
return
}
print(jsonArray)
//Now get title value
guard let title = jsonArray[0]["title"] as? String else { return } print(title)
You get an error because your data contains an array of JSON (dictionary as well), not JSON itself.
So you can't decode with
let JSONData = try JSONDecoder().decode(Dollar_Date.self, from: data)
But you have to do it manually:
EDIT:
guard let json = data as? [[String:Any]] else{return}
Now you can retrieve dollars and date from all the elements of your array:
// These are because you have to access an element of the array
guard let dollar = json[0]["dollar"] as? String else {return}
guard let date = json[0]["date"] as? String else {return}
DispatchQueue.main.async {
self.main_Price.text = dollar
self.data_Label.text = date
}
if your data can contain multiple value (dollars-date couples) you have to loop your array. By the previous example, you should do:
for element in json{
guard let dollar = element["dollar"] as? String else {return}
guard let date = element["date"] as? String else {return}
// do some stuff with each value
}

Trouble getting data from TheMovieDB API with Swift JSON

I am brand new to using JSON and wanted to get started with a simple app to provide a movie overview when you type in a title. My below code returns everything in one big string. How do I get just one piece of information like the overview or year?
With my below attempt, print(obj["overview"] as Any)) prints "nil" and print(obj) looks like this:
{
page = 1;
results = (
{
adult = 0;
"backdrop_path" = "/A0aGxrCGRBuCrDltGYiKGeAUect.jpg";
"genre_ids" = (
53,
80
);
id = 680;
"original_language" = en;
"original_title" = "Pulp Fiction";
overview = "A burger-loving hit man, his philosophical partner, a drug-addled gangster's moll and a washed-up boxer converge in this sprawling, comedic crime caper. Their adventures unfurl in three stories that ingeniously trip back and forth in time.";
Current Code:
let query = "Pulp+Fiction"
let urlString = "https://api.themoviedb.org/3/search/movie?api_key={MYAPIKEY}&query=\(query)"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print(error as Any)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!) as Any
if let obj = parsedData as? NSDictionary {
print(obj["overview"] as Any)
print(obj)
}
} catch {
print("error")
} }
}.resume()
}
// write this extension anywhere in your any swift file
extension String{
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
}
}
//now in your code modify as
if data != nil {
let responseString = String(data: data!, encoding: .utf8)!
if(responseString != "")
{
//convert response string into dictionary using extended method
let responseValues = responseString.toDictionary()
//access value using keyPath using
let value = responseValues.value(forKeyPath: "key.key2")
//where key2 is the target key which is inside the value of key
}
}
First of all JSON results are never Any. As already mentioned in the comments the root object is a dictionary
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any],
The key overview is in array for key results
let results = parsedData["results"] as? [[String:Any]] {
You have to iterate over the array to get the values for key overview
for result in results {
print(result["overview"] as? String ?? "no value for key overview")
}
}
It's highly recommended to use the Codable protocol and custom structs in Swift 4.

Swift 3 - How can i extract elements from a json?

Hello i want to extract certain elements from a json .
CODE: .
var datas:[Usuario]?
struct Usuario : Codable {
let correo: String?
let contrasena: String?
}
let urlString = "http://localhost:8080/swiftdb/logear.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
//Implement JSON decoding and parsing
do {
//Decode retrived data with JSONDecoder and assing type of Article object
let articlesData = try JSONDecoder().decode([Usuario].self, from: data)
//Get back to the main queue
DispatchQueue.main.async {
}
} catch let jsonError {
print(jsonError)
}
}.resume()
What i want to do, is compare what it is in the json with a textfield ,so it can go to a different view.
Json Contains:
[{"correo":"unknown11#hotmail.com","contrasena":"12345"},{"correo":"lalo123#hotmail.com","contrasena":"12121"},{"correo":"kokunz#hotmail.com","contrasena":"11111"},{"correo":"a","contrasena":"a"}]
Textfield Contains: {Something which the user writes in in. txtEmail.text Ex.}
Before the answer, you can check the Apple Reference about JSON and parsing . https://developer.apple.com/swift/blog/?id=37
if let dictionary = netoxJsonVariable as? [String: Any] {
for (key, value) in dictionary {
// access all key / value pairs in dictionary
// I am not sure that you can reach key with string but I think you are searching for this.
//if(value == "searchedMailAddress"){//then}
//if(key == "correoLikeKey"){//then}
}
}
I used a for statement so
let {newVar} = self.{yourJson}
for item in {newVar}! {
let {anotherVar} = item.{field}
let {anotherVar2} = item.{field2}

SWIFT3 JSON NSNull Issue

I have the following swift3 code. The JSON can return a NSNull value for the $0[2] value.
struct Player3 {
let name : String
var score : String
let avatar : String
}
class HistoricLeagueVC: UITableViewController {
var players = [Player3]()
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://str8red.com/jsonoverallleaderboard/1025/"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print("there was an error")
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!) as! [[String]]
self.players = parsedData.map { Player3(name: $0[0], score: $0[1], avatar: $0[2]) }
print(self.players.count)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let error as NSError {
print(error)
}
}
}.resume()
}
I have tried to convert it to an empty string without success. I have also tried to set a default string such as "https://str8red.com/static/no_profile_picture.png" which ideally is what I would like to do.
The error in the terminal states Could not cast value of type 'NSNull' (0xed1c78) to 'NSString' (0x57b6b8).
Any help would be appreciated.
Do not cast to [[String]] then:
let parsedData = try JSONSerialization.jsonObject(with: data!) as! [[Any]]
self.players = parsedData.map { Player3(
name: ($0[0] as? String) ?? "",
score: ($0[1] as? String) ?? "",
avatar: $0[2] as? String, // keeping `nil` here
)}
You should probably even as! [[Any]] do in a safer manner (using as?) but the above will work.
Your code will crash brutally if anything goes wrong with your input data. Ask your boss if he or she is Ok with that. I wouldn't. Here's how you will crash: If data == nil (but it shouldn't be if error == nil). You catch the problem that data cannot be parsed without crashing. You crash if data doesn't parse to an array, you crash if the elements of the array are not all arrays, you crash if the elements inside the inner arrays are not all Strings, and you crash if any of the inner arrays contain two or fewer strings. I'd say that is pretty unacceptable.
My strategy would be: If the response cannot be parsed or is not an array of arrays, then ignore it, leaving self.players unchanged. If there is an array of arrays, reset self.players, then add a player for each array that contains at least three items, of which the first two must be strings.
guard data != nil else { return }
guard let parsed = try? NSJSONSerialization(with: data!) else { return }
guard let arrays = parsed as? [[Any]] else { return }
self.players.removeAll()
for array in arrays {
guard array.count >= 3,
let name = array [0] as? String,
let score = array [1] as? String else { continue }
let avatar = array [2] as? String ?? ""
self.players.append(Player3 (name: name, score: score, avatar: avatar))
}
It's a good idea to move something like this into a separate method. Add whatever error handling or logging you feel is appropriate.

JSON Parsing in Swift 3

Has anyone been able to find a way to parse through JSON files in Swift 3? I have been able to get the data to return but I am unsuccessful when it comes to breaking the data down into specific fields. I would post sample code but I've gone through so many different methods unsuccessfully and haven't saved any. The basic format I want to parse through is something like this. Thanks in advance.
{
"Language": {
"Field":[
{
"Number":"976",
"Name":"Test"
},
{
"Number":"977",
"Name":"Test"
}
]
}
}
Have you tried JSONSerialization.jsonObject(with:options:)?
var jsonString = "{" +
"\"Language\": {" +
"\"Field\":[" +
"{" +
"\"Number\":\"976\"," +
"\"Name\":\"Test\"" +
"}," +
"{" +
"\"Number\":\"977\"," +
"\"Name\":\"Test\"" +
"}" +
"]" +
"}" +
"}"
var data = jsonString.data(using: .utf8)!
let json = try? JSONSerialization.jsonObject(with: data)
Swift sometimes produces some very odd syntax.
if let number = json?["Language"]??["Field"]??[0]?["Number"] as? String {
print(number)
}
Everything in the JSON object hierarchy ends up getting wrapped as an optional (ie. AnyObject?). Array<T> subscript returns a non-optional T. For this JSON, which is wrapped in an optional, array subscript returns Optional<AnyObject>. However, Dictionary<K, V> subscript returns an Optional<V>. For this JSON, subscript returns the very odd looking
Optional<Optional<AnyObject>> (ie. AnyObject??).
json is an Optional<AnyObject>.
json?["Language"] returns an Optional<Optional<AnyObject>>.
json?["Language"]??["Field"] returns an Optional<Optional<AnyObject>>.
json?["Language"]??["Field"]??[0] returns an Optional<AnyObject>.
json?["Language"]??["Field"]??[0]?["Number"] returns an Optional<Optional<AnyObject>>.
json?["Language"]??["Field"]??[0]?["Number"] as? String returns an Optional<String>.
The Optional<String> is then used by the if let syntax to product a String.
Final note: iterating the field array looks like this.
for field in json?["Language"]??["Field"] as? [AnyObject] ?? [] {
if let number = field["Number"] as? String {
print(number)
}
}
Swift 4 Update
Swift 4 makes this all much easier to deal with. Again we will start with your test data (""" makes this so much nicer).
let data = """
{
"Language": {
"Field":[
{
"Number":"976",
"Name":"Test"
},
{
"Number":"977",
"Name":"Test"
}
]
}
}
""".data(using: .utf8)!
Next we can define classes around the objects used in your JSON.
struct Object: Decodable {
let language: Language
enum CodingKeys: String, CodingKey { case language="Language" }
}
struct Language: Decodable {
let fields: [Field]
enum CodingKeys: String, CodingKey { case fields="Field" }
}
struct Field: Decodable {
let number: String
let name: String
enum CodingKeys: String, CodingKey { case number="Number"; case name="Name" }
}
The CodingKeys enum is how struct properties are mapped to JSON object member strings. This mapping is done automagically by Decodable.
Parsing the JSON now is simple.
let object = try! JSONDecoder().decode(Object.self, from: data)
print(object.language.fields[0].name)
for field in object.language.fields {
print(field.number)
}
In Xcode 8 and Swift 3 id now imports as Any rather than AnyObject
This means that JSONSerialization.jsonObject(with: data) returns Any. So you have to cast the json data to a specific type like [String:Any]. Same applies to the next fields down the json.
var jsonString = "{" +
"\"Language\": {" +
"\"Field\":[" +
"{" +
"\"Number\":\"976\"," +
"\"Name\":\"Test1\"" +
"}," +
"{" +
"\"Number\":\"977\"," +
"\"Name\":\"Test2\"" +
"}" +
"]" +
"}" +
"}"
var data = jsonString.data(using: .utf8)!
if let parsedData = try? JSONSerialization.jsonObject(with: data) as! [String:Any] {
let language = parsedData["Language"] as! [String:Any]
print(language)
let field = language["Field"] as! [[String:Any]]
let name = field[0]["Name"]!
print(name) // ==> Test1
}
In practice you would probably want some specific field buried in the json. Lets assume it's the Name field of the first element of Field array. You can use a chain of unwraps like this to safely access the field:
var data = jsonString.data(using: .utf8)!
if let json = try? JSONSerialization.jsonObject(with: data) as? [String:Any],
let language = json?["Language"] as? [String:Any],
let field = language["Field"] as? [[String:Any]],
let name = field[0]["Name"] as? String, field.count > 0 {
print(name) // ==> Test1
} else {
print("bad json - do some recovery")
}
Also you may want to check Apple's Swift Blog Working with JSON in Swift
Shoving JSON into a string manually is a pita. Why don't you just put the JSON into a file and read that in?
Swift 3:
let bundle = Bundle(for: type(of: self))
if let theURL = bundle.url(forResource: "response", withExtension: "json") {
do {
let data = try Data(contentsOf: theURL)
if let parsedData = try? JSONSerialization.jsonObject(with: data) as! [String:Any] {
grok(parsedData)
}
} catch {
print(error)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let url=URL(string:"http://api.androidhive.info/contacts/")
do {
let allContactsData = try Data(contentsOf: url!)
let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
if let arrJSON = allContacts["contacts"] {
for index in 0...arrJSON.count-1 {
let aObject = arrJSON[index] as! [String : AnyObject]
names.append(aObject["name"] as! String)
contacts.append(aObject["email"] as! String)
}
}
print(names)
print(contacts)
self.tableView.reloadData()
}
catch {
}
}
JSON Parsing in swift 4 using Decodable Protocol :
I create a mocky file using your json object :
http://www.mocky.io/v2/5a280c282f0000f92c0635e6
Here is the code to parse the JSON :
Model Creation :
import UIKit
struct Item : Decodable {
// Properties must be the same name as specified in JSON , else it will return nil
var Number : String
var Name : String
}
struct Language : Decodable {
var Field : [Item]
}
struct Result : Decodable {
var Language : Language
}
You can use optional in the model if you are uncertain that something might be missing in JSON file.
This is the parsing Logic :
class ViewController: UIViewController {
let url = "http://www.mocky.io/v2/5a280c282f0000f92c0635e6"
private func parseJSON() {
guard let url = URL(string: url) else { return }
let session = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard let result = try? JSONDecoder().decode(Result.self, from: data) else { return }
print("\n\nResult : \(result)")
}
session.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
parseJSON()
}
}
The Print Output :
Result : Result(Language: JSON_Parsing.Language(Field: [JSON_Parsing.Item(Number: "976", Name: "Test"), JSON_Parsing.Item(Number: "977", Name: "Test")]))
This the github Project link. You can check.
JSON Parsing using Swift 4 in Simple WAY
let url = URL(string: "http://mobileappdevelop.co/TIPIT/webservice/get_my_groups?user_id=5")
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String:Any]
print(json)
let posts = json["Field"] as? [[String: Any]] ?? []
print(posts)
} catch let error as NSError {
print(error)
}
}).resume()
}
Use SwiftJson library. I think its very easy way to parse.
let count: Int? = json["Field"].array?.count
if let ct = count {
for index in 0...ct-1{
let number = json ["Field"][index]["number"].string
let name = json ["Field"][index]["name"].string
....
like this .
dict = {
message = "Login successfully.";
status = 1;
"user_details" = (
{
dob = "1900-11-18";
email = "rizwan#gmail.com";
gender = male;
name = Rizwan;
nickname = Shaikh;
"profile_pic" = "1483434421.jpeg";
"social_id" = "<null>";
"user_id" = 2;
}
);
}
We can parse above json in Swift 3 as
var dict2 = dict as! [String : Any]
print(dict);
let demoStr = dict2["message"] as! String
print(demoStr)
let demoArray = dict2["user_details"] as! [Any]
let demoDict = demoArray[0] as! [String:Any]
print(demoDict["dob"]!)