Error when parse json in swift 4 - json

I want to get data from website by using JSON in my app. But the problem is that when I try to get the value of key ["posts"] in the JSON data, the error appears.
let url = NSURL(string: "http://bongdavn.com/category/du-lieu/?json=1")!
let request = NSMutableURLRequest(url: url as URL)
URLSession.shared.dataTask(with: request as URLRequest) { (data : Data?, urlResponse : URLResponse?, error : Error?) in
if error != nil {
}
do {
let json = try JSONSerialization.jsonObject(with: data!) as? [String : Any]
let post = json!["post"] as! [String : Any]
print(post)
let content = post["content"] // error show from here.
print(content)
// the compiler show nil instead of value of "content"
DispatchQueue.main.async {
self.TableView.reloadData()
}
}
catch {
print("Catch the error : \(error)")
}
}
.resume()
Here is my json data:
{
"status":"ok",
"post":{
"id":121,
"type":"post",
"slug":"epl-england-premier-league-anh",
"url":"http:\/\/bongdavn.com\/epl-england-premier-league-anh\/",
"status":"publish",
"title":"[EPL] England Premier League – Anh",
"title_plain":"[EPL] England Premier League – Anh",
"content":"<p>West Ham <strong><span class=\"hom\">1<\/span>\u00a0–\u00a0<\/strong><span class=\"awy\"><strong>1<\/strong>\u00a0<\/span>Leicester|||Crystal Palace\u00a0<strong><span class=\"hom\">2<\/span>\u00a0–\u00a0<\/strong><span class=\"awy\"><strong>1<\/strong>\u00a0<\/span>Stoke|||,
"date":"2017-11-29 09:44:13",
"modified":"2017-11-29 09:44:16",
"categories":[ ],
"tags":[ ],
"author":{ },
"comments":[ ],
"attachments":[ ],
"comment_count":0,
"comment_status":"open",
"custom_fields":{ }
},
"previous_url":"http:\/\/bongdavn.com\/bundesliga-dortmund-schalke\/"
}
Here is the error:
Could not cast value of type '__NSSingleObjectArrayI' (0x109b9c328) to 'NSDictionary' (0x109b9cf58).
2017-11-29 22:45:19.764169+0700 BongDa[713:17684] Could not cast value of type '__NSSingleObjectArrayI' (0x109b9c328) to 'NSDictionary' (0x109b9cf58).
(lldb)

Your json posted is wrong, according to what I get from the url, there is an array posts, so you can do something like:
do {
let json = try JSONSerialization.jsonObject(with: data!) as? [String : Any]
let posts = json!["posts"] as! [[String: Any]] // posts is an array
let post = posts.first! // get the first here, or according to your need
print(post)
let content = post["content"]
print(content)
DispatchQueue.main.async {
self.TableView.reloadData()
}
}
catch {
print("Catch the error : \(error)")
}
}
.resume()

Related

How to get value of jsonObject inside a jsonObject swift 4?

I use FCM to send push notification to my iOS app.When user click on the notification tray,the data handle by the function below:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print(userInfo['data'])
}
The userInfo is a [AnyHashable:Any] type.I successfully get the data from the userInfo['data'].So here is the data structure for userInfo['data'] :
'{"data":
{
"title":"My app",
"message":"The message here",
"payload":{
"post_id":"602"
},
"timestamp":"2018-03-10 14:12:08"
}
}'
Here is how I tried :
if let dataString = userInfo["data"] as? String {
let data = dataString.data(using: .utf8)!
do {
if let json = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? [String : Any]
{
let message = json["message"] as? String ?? "No message here"
let title = json["title"] as String ?? ""
//here is the problem..I have no idea to do it here
let payload = json["payload"] as? [String : Int] ?? [:]
for element in payload {
if let postId = element["post_id"] {
//print("postId = \(postId)")
}
}
} else {
print("bad json")
}
} catch let error as NSError {
print(error)
}
So as shown in above,I have no problem to get value of title,message and timestamp inside the data json.
But I have to idea how to get the value of post_id which is inside payload array.
So in this case,how to get the value of post_id from the data json above? Thanks.
Access post id like this
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if let data = userInfo["data"] as? [String: Any],
let payload = data["payload"] as? [String: Any],
let postId = payload["post_id"] as? String{
print("post id \(postId)")
}else{
print("there is no post id inside the payload")
}
}

Trouble parsing JSON

I am having trouble parsing some JSON in Swift. I am having trouble getting the errors variable it returns nil. I think it should be a dictionary?
Below is the JSON that is returned from my API as printed in the console.
{
error = "{\"name\":[\"The name has already been taken.\"],\"email\":[\"The email has already been taken.\"]}";
success = 0;
}
And here is the Swift code.
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
print(parseJSON)
let success = parseJSON["success"] as? Int
if(success == 1) {
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default){
(action) in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
} else {
let errors = parseJSON["error"] as? NSDictionary
if(errors != nil){
print("NOT NIL")
// self.displayAlertMessage()
}
}
}
} catch{
print(error)
}
EDIT
Here is the JSON thats is printed using David's code below.
This is the parseJSON printed to the console.
["error": {"name":["The name has already been taken."],"email":["The email has already been taken."]}, "success": 0]
Here is my full method with Davids updated code.
let task = URLSession.shared.dataTask(with: request) { (theData: Data?, response: URLResponse?, theError: Error?) in
DispatchQueue.main.async
{
//spinningActivity!.hide(true)
if theError != nil {
self.displayAlertMessage(theError!.localizedDescription)
return
}
do {
guard let parseJSON = try JSONSerialization.jsonObject(with: theData!) as? [String:Any] else {return}
//print(parseJSON)
let success = parseJSON["success"] as? Int
if(success == 1) {
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default){
(action) in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
} else {
guard let errors = parseJSON["success"] as? Int else {return}
print(errors)
}
} catch{
print(error)
}
}
}
task.resume()
There are several issues with your code that might be not causing the issue directly, but are bad practices. Don't use NSDictionary in Swift, use [String:Any] when decoding JSON responses and don't use .mutableContainers as it has no effect in Swift, the mutability is determined by the let or var keyword when declaring the variable.
Moreover, don't include console print as the JSON response, include the actual JSON response in your question, as Swift's print statement doesn't produce a valid JSON.
let apiErrorResponse = """
{
"error": {
"name": "The name has already been taken.",
"email": ["The email has already been taken."]
},
"success": 0
}
"""
func handleApiErrorResponse(){
do {
guard let parseJSON = try JSONSerialization.jsonObject(with: apiErrorResponse.data(using: .utf8)!) as? [String:Any] else {return}
let success = parseJSON["success"] as? Int
if(success == 1) {
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default){
(action) in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
} else {
guard let errors = parseJSON["error"] as? [String:Any] else {return}
print(errors)
}
} catch{
print(error)
}
}
handleApiErrorResponse()
Output:
"["name": The name has already been taken., "email": <__NSSingleObjectArrayI 0x608000019f80>(\nThe email has already been taken.\n)\n]\n"

Get stuck on JSON data from Wordpress site in Swift 3.0

I'm new to Swift. I am stuck parsing JSON data in my Swift app.
I tried to print out the "title" but it appears nil. Can anyone please help me to solve this problem?
Here is my JSON format:
{
"status":"ok",
"count":2,
"pages":1,
"category":{ },
"posts":[
{
"id":435,
"type":"post",
"slug":"tips-the-thao-ngay-10102017",
"url":"http:\/\/hongbien.online\/tips-the-thao-ngay-10102017\/",
"status":"publish",
"title":"Tips th\u1ec3 thao ng\u00e0y 10\/10\/2017",
"title_plain":"Tips th\u1ec3 thao ng\u00e0y 10\/10\/2017",
"content":"<p>Ph\u00e1p – Belarus<\/p>\n<p>\u0110\u01b0\u1ee3c thi \u0111\u1ea5u tr\u00ean s\u00e2n nh\u00e0 v\u00e0 \u0111\u00e1nh gi\u00e1 v\u01b0\u1ee3t tr\u1ed9i so v\u1edbi \u0111\u1ea5u th\u1ee7 nh\u01b0ng v\u1edbi m\u1ee9c k\u00e8o ch\u1ea5p +3 cho Belarus th\u00ec s\u1ebd r\u1ea5t kh\u00f3 kh\u0103n \u0111\u1ec3 c\u00e1c c\u1ea7u th\u1ee7 Ph\u00e1p chi\u1ec1u l\u00f2ng cho c\u00e1c nh\u00e0 \u0111\u1ea7u t\u01b0 trong b\u1ed1i c\u1ea3nh ch\u1ec9 c\u1ea7n gi\u00e0nh chi\u1ebfn th\u1eafng t\u1ed1i thi\u1ec3u l\u00e0 c\u00f3 th\u1ec3 \u0111i ti\u1ebfp.<\/p>\n<p> <a href=\"http:\/\/hongbien.online\/tips-the-thao-ngay-10102017\/#more-435\" class=\"more-link\">Read more<\/a><\/p>\n",
"excerpt":"<p>Ph\u00e1p – Belarus \u0110\u01b0\u1ee3c thi \u0111\u1ea5u tr\u00ean s\u00e2n nh\u00e0 v\u00e0 \u0111\u00e1nh gi\u00e1 v\u01b0\u1ee3t tr\u1ed9i so v\u1edbi \u0111\u1ea5u th\u1ee7 nh\u01b0ng v\u1edbi m\u1ee9c k\u00e8o ch\u1ea5p +3 cho Belarus th\u00ec s\u1ebd r\u1ea5t kh\u00f3 kh\u0103n \u0111\u1ec3 c\u00e1c c\u1ea7u th\u1ee7 Ph\u00e1p chi\u1ec1u l\u00f2ng cho c\u00e1c nh\u00e0 \u0111\u1ea7u t\u01b0 trong b\u1ed1i c\u1ea3nh ch\u1ec9 c\u1ea7n gi\u00e0nh chi\u1ebfn th\u1eafng t\u1ed1i thi\u1ec3u l\u00e0 c\u00f3 […]<\/p>\n",
"date":"2017-10-11 22:38:20",
"modified":"2017-10-11 22:38:20",
"categories":[ ],
"tags":[ ],
"author":{ },
"comments":[ ],
"attachments":[ ],
"comment_count":0,
"comment_status":"open",
"custom_fields":{ }
},
{ }
]
}
Here is my Swift code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://hongbien.online/category/tips-the-thao/?json=1")!
let request = NSMutableURLRequest(url: url as URL)
URLSession.shared.dataTask(with: request as URLRequest) { (data : Data?, urlResponse : URLResponse?, error : Error?) in
if error != nil {
print(error)
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String : AnyObject]
let title = json["posts"]?["title"]
print(title)
}
catch {
print("Catch the error : \(error)")
}
}
.resume()
}
}
Change this one line. You just forgot to put index for
array(json["posts"] is array)
let title = json["posts"]?[0]["title"]
print(title)

Swift 3 json parsing

I am running into troubles updating my app as Alamofire and SwiftyJSON are not yet supporting Swift 3. I have a url that would return me json as follows:
{
"products": [
{
"body_html":"",
"created_at":"2016-03-02T13:56:18+03:00",
"id":489759251,
"handle":"product",
"options":[
{
"id":627488838,
"product_id":489759251,
"name":"Title",
"position":1,
"values":[
"Default Title"
]
}
],
},
{
"body_html":"",
"created_at":"2016-03-08T05:17:55+03:00",
"id":530420915,
"handle":"product-2",
"options":[
{
"id":6319359750,
"product_id":530420915,
"name":"Title",
"position":1,
"values":[
"Default Title"
]
}
],
},
]
}
I need to be able to parse that json and list all returned products and be able to read any specific attribute and sub options of each.
I checked some other questions here and found several solutions and was able to get the json data and printed it as above. But, I couldn't parse it.
let shopUrl = "https://\(apiKey):\(password)#\(hostname)" + "/admin/products.json"
let url = URL(string: shopUrl)
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
print(json)
} catch let error as NSError {
print(error)
}
}
}).resume()
Any help?
To loop over all of the products you need to extract and cast it to the correct type. In this case an array of [String: Any].
I extracted the relevant bit of code and cleaned it up a bit to make this answer more readable.
guard let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any],
let products = json["products"] as? [[String: Any]]
else { return }
for product in products {
guard let id = product["id"] as? Int,
let options = product["options"] as? [[String: Any]]
else { return }
print(id)
print(options)
}
This parses the JSON, the root object is a dictionary, the objects for products and options are arrays. One value respectively is printed as an example.
if let jsonObject = try JSONSerialization.jsonObject(with:data, options: []) as? [String:Any] {
print(jsonObject)
if let products = jsonObject["products"] as? [[String:Any]] {
for aProduct in products {
print(aProduct["created_at"])
if let options = aProduct["options"] as? [[String:Any]] {
for option in options {
print(option["product_id"])
}
}
}
}
}

How do I get values from a complex JSON object?

Is it possible that someone could show me how to get the names of these pizza places printing out? My application prints out the expected "Status Code: 200". However, my console only shows empty brackets []. I suspect that I am not pulling values from my JSON object properly.
I'm using this link for my API.
Link For API
Question
How can I properly fetch values from my serialized JSON object?
relevant code:
// Response
if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200, let data = data {
print("Status Code: \(httpResponse.statusCode)")
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let pizzaPlaces = json["response"] as? [[String: AnyObject]] {
for place in pizzaPlaces {
if let name = place ["name"] as? String {
self.PizzaClass.append(name)
}
}
}
} catch {
print("Error Serializing JSON Data: \(error)")
}
print(self.PizzaClass)
}
}).resume()
You need to cast your NSJSONSerialization.JSONObjectWithData result as a [String:AnyObject].
let jsonObject = try NSJSONSerialization.JSONObjectWithData(returnedData, options: .MutableLeaves) as! [String: AnyObject]
Once you have that all you need to do is pay attention to what you're casting. Take the code below for an example. If we want to get our response object using jsonObject["response"] what kind of data structure do we have?
"response": {
"venues": [{
//... continues
}]
}
On the left we have "response" which is a string, on the right we have {} which is an AnyObject. So we have [String: AnyObject]. You just need to think about what object your dealing with piece by piece. Below is a working example that you can just paste into your application.
full working code:
func getJson() {
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.foursquare.com/v2/venues/search?client_id=0F5M0EYOOFYLBXUOKTFKL5JBRZQHAQF4HEM1AG5FDX5ABRME&client_secret=FCEG5DWOASDDYII4U3AAO4DQL2O3TCN3NRZBKK01GFMVB21G&v=20130815%20&ll=29.5961,-104.2243&query=burritos")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
guard let testResponse = response as? NSHTTPURLResponse else {
print("\(response)")
return
}
guard let status = HTTPStatusCodes(rawValue: testResponse.statusCode) else {
print("failed to unwrap status")
return
}
print(status)
switch status {
case .Created:
print("ehem")
case .BadRequest:
print("bad request")
case .Ok:
print("ok")
guard let returnedData = data else {
print("no data was returned")
break
}
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(returnedData, options: .MutableLeaves) as! [String: AnyObject]
guard let response = jsonObject["response"] as? [String: AnyObject] else { return }
guard let venues = response["venues"] as? [AnyObject] else { return }
guard let location = venues[0]["location"] as? [String:AnyObject] else { return }
guard let formattedAddress = location["formattedAddress"] else { return }
print("response: \n\n \(response)\n------")
print("venues : \n\n \(venues)\n-------")
print("location : \n\n \(location)\n------")
print("formatted address : \n \(formattedAddress)")
} catch let error {
print(error)
}
// update user interface
dispatch_sync(dispatch_get_main_queue()) {
print("update your interface on the main thread")
}
}
}
task.resume()
}
place this either in its own file our outside of the class declaration,
enum HTTPStatusCodes : Int {
case Created = 202
case Ok = 200
case BadRequest = 404
}
Not that this was what you are looking for, but since you are new to Swift take a look at Alamofire. It handles JSON serialization for you. And when you need to chain calls PromiseKit is super slick.
Alamofire.request(.GET, url).responseJSON {response in
switch (response.result) {
case .Success(let value):
let pizzas = JSON(value).arrayValue
for place in pizzaPlaces {
if let name = place ["name"] as? String {
self.PizzaClass.append(name)
}
}
case .Failure(let error):
if let data = response.data, let dataString = String(data: data, encoding: NSUTF8StringEncoding) {
print("ERROR data: \(dataString)")
}
print("ERROR: \(error)")
}
}