Swift 3 json parsing - json

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

Related

parsing YouTube json with JSONSerialization

I have trouble parsing json data from YouTube api with JSONSerialization. when I try to fetch I return error. this is my code
this is json data I want to parse, I want to get video id, url, title, and desctiption
{
"kind": "youtube#searchListResponse",
"etag": "\"j6xRRd8dTPVVptg711_CSPADRfg/mBDPbwkuU2lLUxWHYPI1X54CUwQ\"",
"nextPageToken": "CAUQAA",
"regionCode": "ID",
"pageInfo": {
"totalResults": 3552,
"resultsPerPage": 5
},
"items": [
{
"kind": "youtube#searchResult",
"etag": "\"j6xRRd8dTPVVptg711_CSPADRfg/73cXngXOrGm_Bt7McNY945A6koc\"",
"id": {
"kind": "youtube#video",
"videoId": "-0ZZzOuuV3c"
},
"snippet": {
"publishedAt": "2018-09-20T08:00:01.000Z",
"channelId": "UCjHoMXZXAIx_QHgk9qsAJ-Q",
"title": "HADIST-HADIST PALSU TAPI POPULER - Ustadz Adi Hidayat LC MA",
"description": "\"Kebersihan sebagian dari iman\". Sering dogn mendengar ucapan ini. Sebagian orang mengatakan ini hadist dari Rasulullah. Tapi taukah kamu, bahwa ini ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/-0ZZzOuuV3c/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/-0ZZzOuuV3c/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/-0ZZzOuuV3c/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "Audio Dakwah",
"liveBroadcastContent": "none"
}
}
This is my code to parse json I create it in youtubeAPI struct, when I try to run it invalidJSONData
static func videos(fromJSON data: Data) -> VideoResults {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
guard
let jsonDictionary = jsonObject as? [AnyHashable: Any],
let itemsArray = jsonDictionary["items"] as? [[String: Any]]
else {
return .failure(YoutubeError.invalidJSONData)
}
var finalItems = [Video]()
for itemJSON in itemsArray {
if let item = video(fromJSON: itemJSON) {
finalItems.append(item)
}
}
if finalItems.isEmpty && !itemsArray.isEmpty {
return .failure(YoutubeError.invalidJSONData)
}
return .success(finalItems)
} catch let error {
return .failure(error)
}
}
private static func video(fromJSON json: [String: Any]) ->Video? {
guard
let videoID = json["videoID"] as? String,
let title = json["title"] as? String,
let description = json["description"] as? String,
let stringURL = json["url"] as? String,
let url = URL(string: stringURL)
else {
return nil
}
return Video(videoID: videoID, title: title, description: description, url: url)
}
with the question I have post. finally I have the answer for my problem. hopefully this will help other developer who want to parse json with JSONSerialization, with such a complex data like YouTube api or other api. so this is my answer.
Since items is an array and inside item have another nested data such as snippet, so I need to iterate snippet to get the data. This is the code.
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
var finalItems = [Video]()
if let items = jsonObject?["items"] as? Array<Dictionary<String, Any>> {
for item in items {
if
let id = item["id"] as? [String: String],
let snippet = item["snippet"] as? [String: Any] {
if
let videoID = id["videoId"],
let description = snippet["description"] as? String,
let title = snippet["title"] as? String,
let thumbnails = snippet["thumbnails"] as? [String: Any],
let medium = thumbnails["medium"] as? [String: Any],
let urlString = medium["url"] as? String,
let url = URL(string: urlString) {
finalItems.append(Video(videoID: videoID, title: title, description: description, url: url))
}
}
}
}
print(finalItems.count)
if finalItems.isEmpty {
return .failure(YoutubeError.invalidJSONData)
}
return .success(finalItems)
} catch let error {
return .failure(error)
}
And the the next step is to clean up the code for parsing that iterate the item, and put it in extension in Video Model, I was found a good article from apple developer website to clean up the code so the code not bloated in my videos function. if you are curious check out this link https://developer.apple.com/swift/blog/?id=37. so the extension for video model is like this.
extension Video {
init(dict: [String: Any]) throws {
guard
let id = dict["id"] as? [String: String],
let videoID = id["videoId"]
else {
throw SerializationError.missing("id")
}
guard
let snippet = dict["snippet"] as? [String: Any],
let description = snippet["description"] as? String,
let title = snippet["title"] as? String,
let thumbnails = snippet["thumbnails"] as? [String: Any],
let medium = thumbnails["medium"] as? [String: Any],
let urlString = medium["url"] as? String,
let url = URL(string: urlString) else { throw SerializationError.missing("snippet") }
self.videoID = videoID
self.title = title
self.description = description
self.url = url
}
}
and then update the videos function, and add do catch block when you try to append since the extension have potential error to parse json data. this is the update method
static func videos(fromJSON data: Data) -> VideoResults {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
var finalItems = [Video]()
if let items = jsonObject?["items"] as? Array<Dictionary<String, Any>> {
for item in items {
do {
try finalItems.append(Video(dict: item))
} catch let error {
print("Failed to append video: \(error)")
}
}
}
print(finalItems.count)
if finalItems.isEmpty {
return .failure(YoutubeError.invalidJSONData)
}
return .success(finalItems)
} catch let error {
return .failure(error)
}
}
and voila the code is safe now, why I not use JSONDecoder? since a lot of people recommended to use JSONDecoder. the reason is, this is my personal reason by the way other might not agree with me. I know how to use JSONDecoder but the thing is i don't want to create a lot struct to decode the json data, since with this JSONSerialization doesn't have to create struct it help me reduce a file and step up my learning curve. and hey even a library like Alamofire have a choice to parse using JSONSerialization. it's up to you now if you want to use JSONDecoder or JSONSerialization I hope this help :).

how to get particular json value in swift

I want the size value in json object but the problem is I'm getting whole json data i want only size value to print
here is my json
[{
size = {
height = 20
width = 10
},
number = 100
}]
here is my code
do{
let Json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
//print(Json as Any)
guard let newValue = Json as? [[String: Any]] else {
print("invalid format")
return
}
print(newValue)
}catch {
print("Response Not Found.")
}
Please learn to read JSON, it's pretty easy, there are only two collection types:
[] is array, Swift [Any] but in almost all cases [[String:Any]], accessed with index subscription.
{} is dictionary, Swift [String:Any], accessed with key subscription
Never use the mutableContainers option in Swift, it has no effect at all.
if let json = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
for item in json {
if let size = item["size"] as? [String:Any] {
print(size["height"], size["width"])
}
}
}
And variable names are supposed to start with a lowercase letter.
PS: You have to cast the type of height and width. The output – which is actually not JSON – is ambiguous, you cannot see if the value is String or Int
You just need to extract size from newValue . Try this
do {
let Json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
guard let newValue = Json as? [[String: Any]],
let size = newValue[0]["size"] as? [String:Any] else {
return
}
print(size)
}
catch {
print("Response Not Found.")
}
guard let newValue = Json as? [[String: Any]] else {
print("invalid format")
return
}
print(newValue["size"])
or if you want height & width
var sizeDict = newValue["size"] as! [String:Any]
print("Width - \(sizeDict["width"])")
print("Width - \(sizeDict["height"])")

Turning JSON file into an array in Swift/Xcode

I have been struggling all week. I am new to programming. I cannot turn a simple JSON file into a dictionary in Xcode. There is little simplified documentation online using the new method of Codable. So I am using a walkthrough, which has the following code.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let path = Bundle.main.path(forResource: "menu", ofType: "json") else { return }
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
//print(json)
guard let array = json as? [Any] else { return }
for user in array {
guard let userDict = user as? [String: Any] else { return }
guard let drinks = userDict["drinks"] as? String else { print("not a String"); return }
guard let junkFood = userDict["junk-food"] as? String else { return }
print(drinks)
print(junkFood)
print(" ")
}
}
catch {
print(error)
}
}
}
The below code is what my JSON looks like.
{"menu": {
"drinks": [
{"coke": "20"},
{"pepsi": "20"},
{"water": "20"}
],
"junk-food": [
{"hamburger": "40"},
{"fries": "20"},
{"pizza": "20"}
]
}}
Can anyone please walk me through, or show me some simplified documentation as to how I can turn the JSON into a dictionary that I can later map the data from? I am using Xcode and trying to work out Swift 4.
Thanks in advance for your patience.
My guess is that your json is actually a Dictionary not an Array. So guard let array = json as? [Any] else { return } is falling through because the json is [String: Any]. You can get to the array with the "menu" key.
Here's an updated version of your code:
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
//print(json)
guard let menuDict = json as? [String: Any] else { return }
guard let drinks = menuDict["drinks"] as? [[String: Any]] else {
print("not an array of dictionaries")
return
}
guard let junkFood = menuDict["junk-food"] as? [[String: Any]] else {
print("not an array of dictionaries")
return
}
print(drinks)
print(junkFood)
print(" ")
}
Try that, let me know if it works. This is just the do block by the way.

Hot to Fix Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members

Trying to get post's 'title','content', categorie's 'title' & author name out of this JSON. getting Error Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members. printing post in console works fine but getting error while trying to get title of post. Please help. JSON & Swith Code is
JSON
{
"status":"ok",
"count":1,
"count_total":44,
"pages":44,
"posts":[
{
"id":87,
"url":"http://www.website.com/blogs/my first blog/",
"title":"My First Blog",
"content":"blog content",
"date":"2015-04-06 22:42:12",
"modified":"2015-12-26 00:45:09",
"categories":[
{
"id":45,
"title":"Trip",
"description":"",
"post_count":21
}
],
"author":{
"id":1,
"name":"admin",
"url":"",
"description":"hello"
}
}
]
}
Swift Code
if let blogContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: blogContent, options: JSONSerialization.ReadingOptions.mutableContainers)
if let items = jsonResult as? [NSString:Any] {
//print(items)
let item = items["posts"] as! NSArray
for post in item {
print(post)
print(post["title"])
}
}
} catch {
print("JSON processing failed.")
}
}
Got it working. Here is the working code. Hope it can help someone with same problem. Thanks :)
if let blogContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: blogContent, options: JSONSerialization.ReadingOptions.mutableContainers)
if let items = jsonResult as? [String: AnyObject] {
if let item = items["posts"] as? NSArray {
for posts in item {
if let post = posts as? [String: AnyObject] {
print(post["title"]!)
let categories = post["categories"] as! NSArray
for category in categories {
if let categ = category as? [String: AnyObject] {
print(categ["title"]!)
}
}
let author = post["author"] as! [String: AnyObject]
print(author["name"]!)
}
}
}

Parsing JSON using Swift 3

I have this json data that I want to consume in Swift 3. I'm learning Swift and building a very basic app that displays the list of items in tableUIView from JSON.
{
"expertPainPanels" : [
{
"name": "User A",
"organization": "Company A"
},
{
"name": "User B",
"organization": "Company B"
}
]
}
I'm trying to get this data using Swift 3.
if (statusCode == 200) {
do{
let json = try? JSONSerialization.jsonObject(with: data!, options:.allowFragments) // [[String:AnyObject]]
/*
If I do this:
let json = try? JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:Any]
if let experts = json?["expertPainPanels"] as! [String: Any] {
I get "Initializer for conditional binding must have Optional type, not '[String: Any]'"
*/
// Type 'Any' has no subscript members.
if let experts = json["expertPainPanels"] as? [String: AnyObject] {
for expert in experts {
let name = expert["name"] as? String
let organization = expert["organization"] as? String
let expertPainPanel = ExpertPainPanel(name: name, organization: organization)!
self.expertPainPanels += [expertPainPanel]
self.tableView.reloadData()
self.removeLoadingScreen()
}
}
}catch {
print("Error with Json: \(error)")
}
}
It was working fine in Swift 2. I updated to Swift 3 which broke the code. I read several SO, but I still have hard time understanding it. I applied some suggestions including JSON Parsing in Swift 3, but I'm still unable to fix the error I'm getting.
As of Swift 3, you need to do a cast early on.
This line:
let json = try? JSONSerialization.jsonObject(with: data!, options:.allowFragments)
Should become this:
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as? [String : AnyObject]
This is because JSONSerialization now returns Any, which does not implement a variation for the [] operator. Make sure you safely unwrap the cast and take the common measures to ensure you don't crash your program.
Edit: Your code should more or less look like this.
let data = Data()
let json = try JSONSerialization.jsonObject(with: data, options:.allowFragments) as! [String : AnyObject]
if let experts = json["expertPainPanels"] as? [String: AnyObject] {