How to parse grouped nested dictionary in swift - ios

Please help me out for parsing the below json response in SWIFT5 as I am getting nil values for the user and group data.
{
"user": {
"0": {
"id": "5",
"name": "ABC"
}
},
"group": {
"0": {
"id": "510",
"name": "XYZ"
}
}
}
if let unwrappedData = data {
do{
let json = try JSONSerialization.jsonObject(with: unwrappedData, options: [])
print(json)
if let user = try? JSONDecoder().decode(UserModel.self,from:unwrappedData){
completion(.success(user))
}else{
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: unwrappedData)
completion(.failure(errorResponse.errorValue))
}
}catch{
completion(.failure(error))
}
}
The user data is printing as nil. please help me out to resolve the issue.

I tried below code in playground and it works like charm, what is the issue in json?
Data Model
// MARK: - Sample
struct Sample: Codable {
let user, group: Group
}
// MARK: - Group
struct Group: Codable {
let the0: The0
enum CodingKeys: String, CodingKey {
case the0 = "0"
}
}
// MARK: - The0
struct The0: Codable {
let id, name: String
}
Json Data
let jsonData = """
{
"user": {
"0": {
"id": "5",
"name": "ABC"
}
},
"group": {
"0": {
"id": "510",
"name": "XYZ"
}
}
}
""".data(using: .utf8)
Json Parsing
if let data = jsonData {
let object = try? JSONDecoder().decode(Sample.self, from: data)
print("Json Object", object)
}
else {
print("Bad Json")
}
Output
Json Object Optional(SwiftPlayground.Sample(user: SwiftPlayground.Group(the0: SwiftPlayground.The0(id: "5", name: "ABC")), group: SwiftPlayground.Group(the0: SwiftPlayground.The0(id: "510", name: "XYZ"))))

Related

Error trying to parse JSON using URLSession in swift

I'm trying to parse a test JSON from a http adress, but I get an error saying that
"No value associated with key CodingKeys(stringValue: \"name\", intValue: nil)
The JSON looks like this. It has been validated, so it should work ok:
{
"work": [
{
"name": "Jocke",
"job": "Developer",
"date": "1985-12-30T00:00:00+0000",
"best_book": {
"id": 33245,
"title": "DiscWorld",
"author": {
"id": 345,
"name": "Terry Prattchet"
}
}
},
{
"name": "Bob",
"job": "Construction worker",
"date": "2010-01-30T00:00:00+0000",
"best_book": {
"id": 375802,
"title": "Ender's Game (Ender's Saga, #1)",
"author": {
"id": 589,
"name": "Orson Scott Card"
}
}
}
]
}
The code looks like this:
struct People: Codable {
let name: String
let job: String
enum OuterKey: String, CodingKey {
case work = "work"
}
enum codingKeys: String, CodingKey {
case name = "name"
case job = "job"
}
init(decoder: Decoder) throws {
let outerContainer = try decoder.container(keyedBy: OuterKey.self)
let innerContainer = try outerContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .work)
self.name = try innerContainer.decode(String.self, forKey: .name)
self.job = try innerContainer.decode(String.self, forKey: .job)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "https://api.myjson.com/bins/fe2eo") else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedJson = try! jsonDecoder.decode([People].self, from: data)
}
}.resume()
}
}
I'm just trying to grasp the first two keys as of now, just to see if it works. But it doesn't even get past name.
Your api returns
[{"firstName":"Jocke","job":"developer"},{"firstName":"Anna","job":"construction"},{"firstName":"Peter","job":"pilot"}]
do {
let jsonDecoder = JSONDecoder()
let decodedJson = try jsonDecoder.decode([People].self, from: data)
}
catch {
print(error)
}
struct People: Codable {
let firstName, job: String
}
just try this
struct Work:Codeable {
let work:[People]
}
struct People: Codable {
let name: String
let job: String
}
do {
let jsonDecoder = JSONDecoder()
let decodedJson = try jsonDecoder.decode(Work.self, from: data)
}
catch {
print(error)
}
if you have same name as json keys you don't need to use codingkeys

Create extension in Swift 5 for Dictonary[String:Any]

I want to create extension for Dictionary [String:Any] which is received from API Response.
Right Now I am doing below way
I have created func getDataFromJson this is working fine, Please let me know how to do that.
func getDataFromJson(json: AnyObject) -> Data?{
do {
print("json = \(json)")
return try JSONSerialization.data(withJSONObject: json, options: JSONSerialization.WritingOptions.prettyPrinted)
} catch let myJSONError {
print("\n\n\nError => getDataFromJson => \(myJSONError)")
}
return nil;
}
This is my response and I want to "data" to Data
{
"status": true,
"message": "Country List",
"data": [
{
"id": 1,
"name": “ABC”,
"code": "A",
"phone_code": "+91”,
"flag": "country-flags/-shiny.png"
},
{
"id": 2,
"name": “ZYX”,
"code": “Z”,
"phone_code": "+1”,
"flag": "country-flags/-shiny.png"
}
]
}
I want to get data this way jsonResponse["data"].retriveData()
Here is a simple function that encodes the dictionary, the function throws any error so it can be properly handled. Since JSONSerialization.data(withJSONObject: takes an Any parameter this function can also be implemented for an array etc
extension Dictionary {
func retriveData() throws -> Data {
return try JSONSerialization.data(withJSONObject: self)
}
}
Simple example
let dict = ["abc": 123, "def": 456]
do {
let data = try dict.retriveData()
let result = try JSONDecoder().decode([String:Int].self, from:data)
print(result)
} catch {
print(error)
}
Another way is to use Result if you're on Swift 5 (shortened after comment from vadian)
extension Dictionary {
func retriveData() -> Result<Data, Error> {
return Result { try JSONSerialization.data(withJSONObject: self) }
}
}
and an example
let result = try dict.retriveData()
switch result {
case .success(let data):
let dictionary = try JSONDecoder().decode([String:Int].self, from:data)
print(dictionary)
case .failure(let error):
print(error)
}
a copy of your function transposed to an extension can be
extension Dictionary {
func retriveData() -> Data? {
do {
return try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
} catch let myJSONError {
print("\n\n\nError => getDataFromJson => \(myJSONError)")
}
return nil
}
}
Correct json
{
"status": true,
"message": "Country List",
"data": [
{
"id": 1,
"name": "ABC",
"code": "A",
"phone_code": "+91",
"flag": "country-flags/-shiny.png"
},
{
"id": 2,
"name": "ZYX",
"code": "Z",
"phone_code": "+1",
"flag": "country-flags/-shiny.png"
}
]
}
Model
struct Datum: Codable {
let id: Int
let name, code, phoneCode, flag: String
}
Decoding data only
let str = """
{
"status": true,
"message": "Country List",
"data": [{
"id": 1,
"name": "ABC",
"code": "A",
"phone_code": "+91",
"flag": "country-flags/-shiny.png"
},
{
"id": 2,
"name": "ZYX",
"code": "Z",
"phone_code": "+1",
"flag": "country-flags/-shiny.png"
}
]
}
"""
do {
let dic = try JSONSerialization.jsonObject(with: Data(str.utf8)) as! [String:Any]
let content = try JSONSerialization.data(withJSONObject: dic["data"])
let dec = JSONDecoder()
dec.keyDecodingStrategy = .convertFromSnakeCase
let res = try dec.decode([Datum].self, from: content)
print(res)
}
catch {
print(error)
}
Please try this
// Response Dictionary
let jsonResponse : [String:Any] = ["data":["key1":"value1",
"key2":"value2",
"key3":"value3",
"key4":"value4"]]
// check dictionary contains value for key "data"
if let dataDict = jsonResponse["data"] as? [String:Any] {
// convert dictionary to data
let jsonData = dataDict.retriveData()
print("Json Data :- ", jsonData != nil ? "Success" : "Data is nil")
}
// Dictionary exxtension for converting dictionary to json data
extension Dictionary {
func retriveData() -> Data? {
do {
print("json = \(self)")
return try JSONSerialization.data(withJSONObject: self, options: JSONSerialization.WritingOptions.prettyPrinted)
} catch let myJSONError {
print("\n\n\nError => getDataFromJson => \(myJSONError)")
}
return nil;
}
}

Unable to parse array of dictionary inside string

Currently I am working on bus booking module. After user set his departure and arrival city and date of journey, user will be shown a list of available buses. I have done that part successfully. But the problem which I am facing is that, each bus has it's own cancellation policy which is array of dictionary inside string. I am unable to parse it. Inside every dictionary of "apiAvailableBuses" there is "cancellationPolicy" key which has string as value which contains array of dictionary. I have removed other key value pairs from "apiAvailableBuses".
List of available Buses JSON response:
"apiAvailableBuses":[
{
"cancellationPolicy":"[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
},
{
"cancellationPolicy":"[{\"cutoffTime\":\"9-12\",\"refundInPercentage\":\"25\"},{\"cutoffTime\":\"12-24\",\"refundInPercentage\":\"35\"},{\"cutoffTime\":\"24-48\",\"refundInPercentage\":\"50\"},{\"cutoffTime\":\"48-60\",\"refundInPercentage\":\"75\"},{\"cutoffTime\":\"60\",\"refundInPercentage\":\"90\"}]"
},
{
"cancellationPolicy":"[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
},
{
"cancellationPolicy":"[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
},
{
"cancellationPolicy":"[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
},
{
"cancellationPolicy":"[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
},
{
"cancellationPolicy":"[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
},
{
"cancellationPolicy":"[{\"cutoffTime\":\"6-24\",\"refundInPercentage\":\"70\"},{\"cutoffTime\":\"24\",\"refundInPercentage\":\"85\"}]"
}
]
Can anyone help me with a solution for this? If anyone can't understand my question please let me know.
Note: I am not using Codable in my project.
Thanks in advance.
Use Codable to parse the above JSON response.
If your JSON response have the below format:
{
"apiAvailableBuses": [
{
"cancellationPolicy": [
{
"cutoffTime": "5",
"refundInPercentage": "90"
}
]
}
]
}
Create Codable types to parse the above response.
struct AvailableBuses: Codable {
var apiAvailableBuses: [Bus]
}
struct Bus: Codable {
var cancellationPolicy: [CancellationPolicy]
}
struct CancellationPolicy: Codable {
var cutoffTime: String
var refundInPercentage: String
}
In the above code, I've created 3 struct conforming to Codable protocol - AvailableBuses, Bus, CancellationPolicy
Usage:
After getting data from your API response, you can parse the it using the above created structs like,
if let data = jsonStr.data(using: .utf8) {
do {
let availableBuses = try JSONDecoder().decode(AvailableBuses.self, from: data)
print(availableBuses)
} catch {
print(error)
}
}
If you don't want to use Codable for some reason, you can use JSONSerialization.
let input = "[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
let data = input.data(using: .utf8)!
let parsed = try! JSONSerialization.jsonObject(with: data, options: []) as! Array<Dictionary<String, Any>>
print(parsed) // [["refundInPercentage": 90, "cutoffTime": 5]]
You can parse JSON string using the following method:
// JSON Format
let jsonResponse = ["apiAvailableBuses": [
[
"cancellationPolicy": "[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
],
[
"cancellationPolicy": "[{\"cutoffTime\":\"9-12\",\"refundInPercentage\":\"25\"},{\"cutoffTime\":\"12-24\",\"refundInPercentage\":\"35\"},{\"cutoffTime\":\"24-48\",\"refundInPercentage\":\"50\"},{\"cutoffTime\":\"48-60\",\"refundInPercentage\":\"75\"},{\"cutoffTime\":\"60\",\"refundInPercentage\":\"90\"}]"
],
[
"cancellationPolicy": "[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
],
[
"cancellationPolicy": "[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
],
[
"cancellationPolicy": "[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
],
[
"cancellationPolicy": "[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
],
[
"cancellationPolicy": "[{\"cutoffTime\":\"5\",\"refundInPercentage\":\"90\"}]"
],
[
"cancellationPolicy": "[{\"cutoffTime\":\"6-24\",\"refundInPercentage\":\"70\"},{\"cutoffTime\":\"24\",\"refundInPercentage\":\"85\"}]"
]
]
]
// Function Calling
setBuses(json: jsonResponse)
// Function to Parse JSON
func setBuses(json: Dictionary<String,Any>) {
guard let buses = json["apiAvailableBuses"] as? [Dictionary<String,Any>] else { return }
for (index, bus) in buses.enumerated() {
print("\n\nBus #\(index+1)")
guard let policies = convertToDictionary(text: bus["cancellationPolicy"] as! String) else { return }
for (index, policy) in policies.enumerated() {
print("\nPolicy #\(index+1)")
print("cutoffTime #\(index+1) \(String(describing: policy["refundInPercentage"]))")
print("refundInPercentage #\(index+1) \(String(describing: policy["cutoffTime"]))")
}
}
}
func convertToDictionary(text: String) -> [Dictionary<String,Any>]? {
let data = text.data(using: .utf8)!
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? [Dictionary<String,Any>] {
return jsonObj
} else {
print("JSON Error")
}
} catch let error as NSError {
print(error)
}
return nil
}

How to parse json data include page number by codable in Swift 4 and Alamofire

I'm a new with Swift and I'm confusing to get data instead of using SwiftyJson by using Codable.
The format Json data type like as:
{
"current_page": 1,
"total_page": 407,
"new_entries": [
{
"id": 10174,
"title": "Hello",
"description": "Hello",
"categories": "women",
"image": "imagelink",
"url": "urllink",
"date": "time",
"is_favorite": false
},
{
"id": 9237,
"title": "hi",
"description": "hi",
"categories": "skincare",
"image": "imagelink",
"url": "url",
"date": "time",
"is_favorite": false
},
So how do I get the entries and decode and save to codable like
let decoder = JSONDecoder()
do {
let feed = try decoder.decode(Feed.self, from: jsonData)
print(feed.title)
} catch {
print(error.localizedDescription)
}
I stop at this below and dont know how to convert the json["new_entries"] to string type and decode.
Alamofire.request("https://abc.jp/api/category/women_all/?page=1", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
debugPrint(response)
if let JSON = response.result.value as? NSDictionary {
GlobalVariables.sharedManager.pageCurr = JSON["current_page"] as? Int
GlobalVariables.sharedManager.pageTotal = JSON["total_page"] as? Int
if let entries = JSON["new_entries"] as? NSArray{
for entry in entries {
if let entry = entry as? NSDictionary {
for (key, value) in entry {
print("\(key) - \(value)")
}
}
}
}
}
My Feed Struct
struct Feed: Codable {
enum CodingKeys: String, CodingKey {
case id
case title
case description
case categories
case image
case url
case date
case favorite = "is_favorite"
}
let id: Int
let title: String
let description: String
let categories: String
let image: String
let url: String
let date: String
let favorite: Bool
}
Thanks so much.
You need
struct Root: Codable {
let currentPage, totalPage: Int
let newEntries: [Feed]
}
struct Feed: Codable {
let id: Int
let title, description, categories, image: String
let url, date: String
}
Alamofire.request("https://abc.jp/api/category/women_all/?page=1", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseData { response in
debugPrint(response)
guard let data = response.data else { return }
do {
let de = JSONDecoder()
de.keyDecodingStrategy = .convertFromSnakeCase
let res = try de.decode(Root.self, from: data)
print(res.currentPage)
print(res.totalPage)
print(res.newEntries)
}
catch {
print(error)
}
}
correct json
{
"current_page": 1,
"total_page": 407,
"new_entries": [
{
"id": 10174,
"title": "Hello",
"description": "Hello",
"categories": "women",
"image": "imagelink",
"url": "urllink",
"date": "time",
"is_favorite": false
},
{
"id": 9237,
"title": "hi",
"description": "hi",
"categories": "skincare",
"image": "imagelink",
"url": "url",
"date": "time",
"is_favorite": false
}]
}
I added a new main struct
struct Main: Codable {
let currentPage: Int
let totalPage: Int
let feeds: [Feed]
enum CodingKeys: String, CodingKey {
case currentPage = "current_page"
case totalPage = "total_page"
case feeds = "new_entries"
}
}
and then decode using that struct
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Main.self, from: jsonData)
print(result.currentPage)
} catch {
print(error)
}
or with the alamofire example
Alamofire.request("https://abc.jp/api/category/women_all/?page=1", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseData { response in
guard let data = response.data else { return }
do {
let decoder = JSONDecoder()
let result = try decoder.decode(Main.self, from: data)
GlobalVariables.sharedManager.pageCurr = result.currentPage
GlobalVariables.sharedManager.pageTotal = result.totalPage
for feed in result.feeds {
print(feed.id)
//etc
}
} catch {
print(error)
// other error handling
}

how to show titles(Type) in table view using JSON Data in iOS Swift?

how to show titles(Type) in table view using JSON Data in iOS Swift using the following format
[
{
"Id": 11000,
"Type": "Title1"
},
{
"Id": 11001,
"Type": "Title2"
},
{
"Id": 11002,
"Type": "Title3"
},
{
"Id": 11003,
"Type": "Title4"
}
]
You can use the following function to read titles in an array and use it as data source for tableView.
private func readJson( data : Data) -> [String] {
var titlesArray = [String]()
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let jsonDict = json as? [[String: Any]] {
for anObject in jsonDict {
if let title = anObject["Type"] as? String {
titlesArray.append(title)
}
}
} else {
print("JSON is invalid")
}
} catch {
print(error.localizedDescription)
}
return titlesArray
}
I would suggest to use SwiftyJson for json parsing. Instead of doing it manually. You can read about Swifty Json.

Resources