Not able to parse api response - ios

I have an api response that looks like so…I get the response from the api properly..
{
"status": "success",
"data": {
"meta": {
"url": "htt..://www.abc.com",
"title": “ASD - Log In or Sign Up",
"description": "Create an account or log in….”,
"display_url": "htt..://www.abc.com/",
"video_url": "",
"image": "htt..://www.asd.com/images/asds_325x325.png",
"img_wxh": "325x325"
}
}
}
The model class with which I parse this data is given like so..
struct MetaData: Codable {
let status: String?
let data: DataClass?
}
struct DataClass: Codable {
let meta: Meta
}
struct Meta: Codable {
let url: String
let title, description: String
let displayURL: String
let videoURL: String
let image: String
let imgWxh: String
enum CodingKeys: String, CodingKey {
case url = "url"
case title = "title"
case description = "description"
case displayURL = "display_url"
case videoURL = "video_url"
case image = "image"
case imgWxh = "img_wxh"
}
}
The api call that is being made is gives as below...
WebServiceClient.shared.getMeta(withParameters: parameters) { [weak self] (isSuccess, result) in
guard let `self` = self else { return }
if isSuccess, result != nil {
if let jsonData = try? JSONSerialization.data(withJSONObject: result as Any, options: []) {
do {
let metaData = try JSONDecoder().decode(MetaData.self, from: jsonData)
self.metaDataImageView.sd_setImage(with: URL(string: metaData.data?.meta.image ?? ""), completed: nil)
self.urlLabel.text = metaData.data?.meta.url
self.titleLabel.text = metaData.data?.meta.title
self.urlDescriptionLabel.text = metaData.data?.meta.description
} catch {
print("error \(error)")
}
}
}
But I get all data as nil...what could be the reason..?
I get nothing in metaData...

Here is the code I tried to parse your data
struct MetaData: Codable {
let status: String?
let data: DataClass?
}
struct DataClass: Codable {
let meta: Meta
}
struct Meta: Codable {
let url: String
let title, description: String
let displayURL: String
let videoURL: String
let image: String
let imgWxh: String
enum CodingKeys: String, CodingKey {
case url = "url"
case title = "title"
case description = "description"
case displayURL = "display_url"
case videoURL = "video_url"
case image = "image"
case imgWxh = "img_wxh"
}
}
let jsonString = """
{
"status": "success",
"data": {
"meta": {
"url": "htt..://www.abc.com",
"title": "ASD - Log In or Sign Up ",
"description": "Create an account or log in….",
"display_url": "htt..://www.abc.com/",
"video_url": "",
"image": "htt..://www.asd.com/images/asds_325x325.png",
"img_wxh": "325x325"
}
}
}
"""
let jsonData = jsonString.data(using: .utf8)
do {
let parsedData = try JSONDecoder().decode(MetaData.self, from: jsonData!)
print(parsedData)
} catch {
print(error.localizedDescription)
}
And it works.
Also your json have some issue so make sure you validate your json format.
you can use jsonlint.com for validating json.

Related

Swift 5: Decoding Nested JSON

I'm having a little trouble decoding some JSON data into a struct. I've tried below methods and it doesn't work:
JSON:
{
"submission_date": "2020-02-28T14:21:46.000+08:00",
"status": "pending",
"requestor": {
"name": "Adam"
},
"claim_items": [
{
"date": "2020-02-20",
"description": "TV",
"currency": "MYR",
"amount": "103.0",
"amount_in_ringgit": "10.0"
},
{
"date": "2020-02-20",
"description": "Netflix",
"currency": "MYR",
"amount": "12.0",
"amount_in_ringgit": "10.0"
}
]
}
Struct Method 1:
struct ClaimDetail: Decodable {
let submission_date: String
let status: String
let requestor: Requestor
let claim_items: [ClaimItem]
}
struct Requestor: Decodable {
let name: String
init(json: [String:Any]) {
name = json["name"] as? String ?? ""
}
}
struct ClaimItem: Decodable {
let date: String
let description: String
let currency: String
let amount: String
let amount_in_ringgit: String
init(json: [String:Any]) {
date = json["date"] as? String ?? ""
description = json["description"] as? String ?? ""
currency = json["currency"] as? String ?? ""
amount = json["amount"] as? String ?? ""
amount_in_ringgit = json["amount_in_ringgit"] as? String ?? ""
}
}
Struct Method 2:
struct ClaimDetail: Decodable {
let submission_date: String
let status: String
let requestor: Requestor
let claim_items: [ClaimItem]
struct Requestor: Decodable {
let name: String
init(json: [String:Any]) {
name = json["name"] as? String ?? ""
}
}
struct ClaimItem: Decodable {
let date: String
let description: String
let currency: String
let amount: String
let amount_in_ringgit: String
init(json: [String:Any]) {
date = json["date"] as? String ?? ""
description = json["description"] as? String ?? ""
currency = json["currency"] as? String ?? ""
amount = json["amount"] as? String ?? ""
amount_in_ringgit = json["amount_in_ringgit"] as? String ?? ""
}
}
}
Struct Method 3 (via https://app.quicktype.io/):
// MARK: - ClaimDetail
struct ClaimDetail: Codable {
let submissionDate, status: String
let requestor: Requestor
let claimItems: [ClaimItem]
enum CodingKeys: String, CodingKey {
case submissionDate = "submission_date"
case status, requestor
case claimItems = "claim_items"
}
}
// MARK: - ClaimItem
struct ClaimItem: Codable {
let date, claimItemDescription, currency, amount: String
let amountInRinggit: String
enum CodingKeys: String, CodingKey {
case date
case claimItemDescription = "description"
case currency, amount
case amountInRinggit = "amount_in_ringgit"
}
}
// MARK: - Requestor
struct Requestor: Codable {
let name: String
}
URL Session
URLSession.shared.dataTask(with: requestAPI) { [weak self] (data, response, error) in
if let data = data {
do {
let json = try JSONDecoder().decode(ClaimDetail.self, from: data)
print (json)
} catch let error {
print("Localized Error: \(error.localizedDescription)")
print("Error: \(error)")
}
}
}.resume()
All returns below error:
Localized Error: The data couldn’t be read because it isn’t in the correct format.
Error: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
Solution:
I used struct method #1 and it wasn't the issue. The issue was with how I decoded the data in URLSession. For some reason, this works:
URLSession.shared.dataTask(with: requestAPI) { [weak self] (data, response, error) in
if let data = data {
do {
let dataString = String(data: data, encoding: .utf8)
let jsondata = dataString?.data(using: .utf8)
let result = try JSONDecoder().decode(ClaimDetail.self, from: jsondata!)
print(result)
} catch let error {
print("Localized Error: \(error.localizedDescription)")
print("Error: \(error)")
}
}
}.resume()
Screenshot:
I don't really understand but I guess I had to convert the data into a string, then decode that instead?
Thank you all for helping out.

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

Parsing json data using Codable

I am new to using Codable for parsing data from JSON and I am having trouble with the format of my JSON. I am not able to parse the correct fields into my Employee object. This is my first time using codable and dealing with a complex URL. This is how my JSON url is structured: https://ibb.co/WgDNMNT
{
"students": [
{
"uuid": "0djkdjjf734783749c",
"full_name": "Joe Morris",
"phone_number": "44445399",
"email_address": "jm99#jfgj.com",
"biography": "student of arts"
},
{
"uuid": "0djkdjjf734783749c",
"full_name": "Joe Morris",
"phone_number": "44445399",
"email_address": "jm99#jfgj.com",
"biography": "student of arts"
}
]
}
Here is my code:
struct Students: Codable {
var uuid: String?
var fullName: String?
var phoneNumber: String?
var emailAddress: String?
var biography: String?
}
//Custom Keys
enum CodingKeys: String, CodingKey{
case uuid
case fullname = "full_name"
case phoneNumber = "phone_number"
case emailAddress = "email_address"
case biography = "biography"
}
func parseData(){
guard let url = URL(string: "xxxxxxxxxx") else {return}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Error")
return }
do{
let decoder = JSONDecoder()
let model = try decoder.decode([Students].self, from: dataResponse)
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
Replace
let model = try decoder.decode([Students].self, from: dataResponse)
With
let model = try decoder.decode([String:[Students]].self, from: dataResponse)
print(model["students"])

Getting an error decoding JSON, Swift 4.2

I am getting an error when decoding JSON in swift 4.2
Expected to decode Array but found a dictionary instead.
My JSON Model:
public struct NewsSource: Equatable, Decodable {
public let id: String?
public let name: String?
public let sourceDescription: String?
public let url: URL?
enum CodingKeys: String, CodingKey {
case id
case name
case sourceDescription = "description"
case url
}
public init(id: String,
name: String,
sourceDescription: String,
url: URL,
category: NewsCategory,
language: NewsLanguage,
country: NewsCountry) {
self.id = id
self.name = name
self.sourceDescription = sourceDescription
self.url = url
} }
How I fetch the JSON:
func fetchJSON() {
let urlString = "https://newsapi.org/v2/sources?apiKey=myAPIKey"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, err) in
DispatchQueue.main.async {
if let err = err {
print("Failed to get data from url:", err)
return
}
guard let data = data else { return }
print(data)
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.Sources = try decoder.decode([NewsSource].self, from: data)
self.tableView.reloadData()
} catch let jsonErr {
print("Failed to decode:", jsonErr)
}
}
}.resume()
}
If you look at the JSON that is being returned it looks like this:
{
"status": "ok",
"sources": [{
"id": "abc-news",
"name": "ABC News",
"description": "Your trusted source for breaking news, analysis, exclusive interviews, headlines, and videos at ABCNews.com.",
"url": "https://abcnews.go.com",
"category": "general",
"language": "en",
"country": "us"
}, {
"id": "abc-news-au",
"name": "ABC News (AU)",
"description": "Australia's most trusted source of local, national and world news. Comprehensive, independent, in-depth analysis, the latest business, sport, weather and more.",
"url": "http://www.abc.net.au/news",
"category": "general",
"language": "en",
"country": "au"
},
...
While there is an array of sources, the array is not the root. The root of the JSON is an object with a status string and and a sources array. This is why the decoder is failing.
You need to define an additional struct to handle this:
struct NewsResult {
let status: String
let sources: [NewsSource]
}
Then you decode this object:
let sourceResult = try decoder.decode(NewsResult.self, from: data)
self.sources = sourceResult.sources
This should be your structure:
struct NewsSource: Codable {
let status: String
let sources: [NewsSource]
}
public struct NewsSource: Equatable, Decodable {
public let id: String?
public let name: String?
public let sourceDescription: String?
public let url: URL?
enum CodingKeys: String, CodingKey {
case id
case name
case sourceDescription = "description"
case url
}
public init(id: String,
name: String,
sourceDescription: String,
url: URL,
category: NewsCategory,
language: NewsLanguage,
country: NewsCountry) {
self.id = id
self.name = name
self.sourceDescription = sourceDescription
self.url = url
} }
struct Source: Codable {
let id, name, description: String
let url: String
let category: Category
let language, country: String
}
enum Category: String, Codable {
case business = "business"
case entertainment = "entertainment"
case general = "general"
case health = "health"
case science = "science"
case sports = "sports"
case technology = "technology"
}
And then to decode it:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let newsSource = try? decoder.decode(NewsSource.self, from: data)
self.Sources = newsSource.sources
self.tableView.reloadData()
Hope this helps!

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
}

Resources