Getting data append problem in nested json by using swiftjson library swift.
I have created two struct but getting an error while appending the final list. Error in getting when appending data. Have i created struct well.
My struct
struct GistModel {
var comments : Int!
var commentsUrl : String!
var descriptionField : String!
var owner : Owner!
}
struct Owner{
var login : String!
}
JSON DATA result:
{
url: "https://api.github.com/gists/7e624eed62b3a317541791d719dcacf2",
forks_url: "https://api.github.com/gists/7e624eed62b3a317541791d719dcacf2/forks",
commits_url: "https://api.github.com/gists/7e624eed62b3a317541791d719dcacf2/commits",
id: "7e624eed62b3a317541791d719dcacf2",
node_id: "MDQ6R2lzdDdlNjI0ZWVkNjJiM2EzMTc1NDE3OTFkNzE5ZGNhY2Yy",
git_pull_url: "https://gist.github.com/7e624eed62b3a317541791d719dcacf2.git",
git_push_url: "https://gist.github.com/7e624eed62b3a317541791d719dcacf2.git",
html_url: "https://gist.github.com/7e624eed62b3a317541791d719dcacf2",
files:
{
GistTest2:
{
filename: "GistTest2",
type: "text/plain",
language: null,
raw_url: "https://gist.githubusercontent.com/MasamMahmood/7e624eed62b3a317541791d719dcacf2/raw/7302f0d923e9e08b0e502ad9df762a1b2aa072e1/GistTest2",
size: 29
}
},
public: true,
created_at: "2019-02-01T18:41:39Z",
updated_at: "2019-02-01T19:01:16Z",
description: "Gist Test 2",
comments: 0,
user: null,
comments_url: "https://api.github.com/gists/7e624eed62b3a317541791d719dcacf2/comments",
owner:
{
login: "MasamMahmood",
id: 36441313,
node_id: "MDQ6VXNlcjM2NDQxMzEz",
avatar_url: "https://avatars3.githubusercontent.com/u/36441313?v=4",
gravatar_id: "",
url: "https://api.github.com/users/MasamMahmood",
html_url: "https://github.com/MasamMahmood",
followers_url: "https://api.github.com/users/MasamMahmood/followers",
following_url: "https://api.github.com/users/MasamMahmood/following{/other_user}",
gists_url: "https://api.github.com/users/MasamMahmood/gists{/gist_id}",
starred_url: "https://api.github.com/users/MasamMahmood/starred{/owner}{/repo}",
subscriptions_url: "https://api.github.com/users/MasamMahmood/subscriptions",
organizations_url: "https://api.github.com/users/MasamMahmood/orgs",
repos_url: "https://api.github.com/users/MasamMahmood/repos",
events_url: "https://api.github.com/users/MasamMahmood/events{/privacy}",
received_events_url: "https://api.github.com/users/MasamMahmood/received_events",
type: "User",
site_admin: false
},
truncated: false
}
Swift:
switch response.result{
case .success(let value):
let json = JSON(value)
print(json)
for subJson in json.arrayValue {
let comm = subJson["comments"].intValue
let commurl = subJson["comments_url"].stringValue
let desc = subJson["description"].string
//let age = subJson["owner"]["login"].string
for item in subJson{
let login = subJson["owner"]["login"].string
// do something
}
let user = GistModel(comments: comm, commentsUrl: commurl, descriptionField: desc, login: login)//, owner: login)
self.DataList.append(user)
print(user)
}
I am newbie Getting error on append the list. "Use of unresolved identifier 'login'".
If you are willing to move to standard json handling using Codable then this will work. First let structs implement Decodable
struct GistModel: Decodable {
let comments: Int
let commentsUrl: String
let description: String //Changed the name here
let owner: Owner
}
struct Owner: Decodable {
let login: String
}
And encoding is done like this
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode([GistModel].self, from: data)
print(result[0].owner.login)
print(result[0].comments)
print(result[0].commentsUrl)
} catch {
print(error)
}
Related
I am trying to archive data and want to store it in userdefault but app getting crash.
Also tried this
let encodedData = try NSKeyedArchiver.archivedData(withRootObject: selectedPoductDetails, requiringSecureCoding: false)
selectedPoductDetails is dict of type [String: SelectedProductDetail]
import Foundation
class SelectedProductDetail {
let product: String
var amount: Double
var title: String
init(product: String, amount: Double, title: String ) {
self.product = product
self.amount = amount
self.title = title
}
}
May i know why its not working and possible solution for same?
For this case you can use UserDefaults
struct ProductDetail: Codable {
//...
}
let encoder = JSONEncoder()
let selectedProductDetails = ProductDetail()
// Set
if let data = try? encoder.encode(selectedProductDetails) {
UserDefaults.standard.set(data, forKey: "selectedProductDetails")
}
// Get
if let selectedProductDetailsData = UserDefaults.standard.object(forKey: "selectedProductDetails") as? Data {
let selectedProductDetails = try? JSONDecoder().decode(ProductDetail.self, from: selectedProductDetailsData)
}
As mentioned in the comments to use NSKeyedArchiver the class must adopt NSSecureCoding and implement the two required methods.
The types in your class are JSON compatible, so adopt Codable and archive the data with JSONEncoder (or PropertyListEncoder). You could even use a struct and delete the init method
struct SelectedProductDetail: Codable {
let product: String
var amount: Double
var title: String
}
var productDetails = [String: SelectedProductDetail]()
// populate the dictionary
do {
let data = try JSONEncoder().encode(productDetails)
UserDefaults.standard.set(data, forKey: "productDetails")
} catch {
print(error)
}
And load it
do {
guard let data = UserDefaults.standard.data(forKey: "productDetails") else { return }
productDetails = try JSONDecoder().decode([String: SelectedProductDetail].self, from: data)
} catch {
print(error)
}
Note:
UserDefaults is the wrong place for user data. It's better to save the data in the Documents folder
I'm using the tableview to display the Two Json value but the problem is I cant add value into model struct to displaying into tableview using two Api's. i want to show percentage value in one of the cell label and
here is my json
[
{
"Percentage": 99.792098999,
}
]
my second json value
{
"Categories": [
"Developer",
"ios "
],
"Tags": [
{
"Value": "kishore",
"Key": "Name"
},
{
"Value": "2",
"Key": "office"
},
]
}
and i need show the Categories value in Categories label in tableview
value and key on tableview
here is my Struct
struct info: Decodable {
let Categories: String?
let Tags: String?
let Value: String?
let Key: String?
var Name: String?
let percentage: Double?
}
here its my code
var List = [info]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
print(json as Any)
guard let jsonArray = json as? [[String: Any]] else {
return
}
print(jsonArray)
for dic in jsonArray{
guard let per = dic["percentage"] as? Double else { return }
print(per)
}
and second json
if let array = json["Tags"] as? [[String: String]] {
for dict in array {
let key = dict["Key"]
let value = dict["Value"]
switch key {
case "office":
case "Name":
default:
break;
}
}
here is my cell for row indexpath
cell.Categories.text = list[indexpath.row].percentage
cell.Name.text = list[indexpath.row].name
cell.office.text = list[indexpath.row].office
Please use Swift 4 Codable protocol to decode the value from JSON.
//1.0 Create your structures and make it conform to Codable Protocol
struct Tags: Codable{
var Key: String
var Value: String
}
struct Sample: Codable{
var Categories: [String]
var Tags: [Tags]
}
In your method, perform below steps:
//2.0 Get your json data from your API. In example below, i am reading from a JSON file named "Sample.json"
if let path = Bundle.main.path(forResource: "Sample", ofType: "json") {
do {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
do {
//3.0 use JSONDecoder's decode method to decode JSON to your model.
let sample = try JSONDecoder().decode(Sample.self, from: jsonData)
//4.0 User the "sample" model to do your stuff. Example, printing some values
print("Sample.Category = \(sample.Categories)")
print("Sample.Name = \(sample.Tags[0].Value)")
print("Sample.Office = \(sample.Tags[1].Value)")
} catch let error {
print("Error = \(error)")
}
} catch {
// handle error
}
}
I prefer to use Codable all the time with JSON even for simpler types so for percentage I would do
struct ItemElement: Decodable {
let percentage: Double
enum CodingKeys: String, CodingKey {
case percentage = "Percentage"
}
}
and we need to keep these values in a separate array, declared as a class property
let percentageList: [Double]()
and json encoding would then be
let decoder = JSONDecoder()
do {
let result = try decoder.decode([ItemElement].self, from: data)
percentageList = result.map { item.percentage }
} catch {
print(error)
}
Similar for the second part
struct Item: Decodable {
let categories: [String]
let tags: [Tag]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
case tags = "Tags"
}
}
struct Tag: Decodable {
let value, key: String
enum CodingKeys: String, CodingKey {
case value = "Value"
case key = "Key"
}
}
use a dictionary for the result, again as a class property
var values = [String: String]()
and the decoding
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Item.self, from: data)
for item in result.tags {
values[item.key] = values.item.value
}
} catch {
print(error)
}
and then in the cell for row code
cell.Categories.text = percentageList[indexpath.row].percentage
cell.Name.text = values["name"]
cell.office.text = values["office"]
Note that this last code looks very strange since you don't have an array of name/office values judging by your json. Maybe you have simplified it some way but the code above is the best I can do with the information given even if it possibly wrong
It's my first post. So far I've always found my answers myself, but today, after plenty hours of research, I'm still hitting my head against a wall...
Here is my problem, I get a JSON from a Alamofire request and I cannot parse my data as I want to.
I have this answer :
{"family": {"id":1, "name": "myFamily","members": [{"userId":15, "lasName": "COLINET", "firstName":"Steve","latitude":48.290762, "longitude":4.070 },{"userId":18,"lasName":"BERTHIER","firstName":"Renaud","latitude":48.290755, "longitude":4.071 }]}}
I want to get a list of members in which I can pickup the firstname/lastname and latitude/longitude.
I've tried so much things that I can't enumerate...
Here is the (bad) code that I have right now :
func getMembers(username:String, password:String){
var members:NSDictionary = [:]
let parameters: Parameters=[
"action":"getPosition",
"username":"\(username)",
"password":"\(password)"
]
Alamofire.request(login_url, method: .post, parameters: parameters).responseJSON{
response in
switch response.result {
case .success(let data):
guard let json = data as? [String : AnyObject] else { return }
members = json["family"]!["members"] as! NSDictionary
members.forEach { member in
print(member["firstName"])
}
case .failure(let error):
print(error)
}
}
}
Thank's for your help.
members is an array not dictionary
if let family = json["family"] as? [String:Any] {
if let members = family["members"] as? [[String:Any]] {
print(members)
}
}
it would be better using
struct Root: Codable {
let family: Family
}
struct Family: Codable {
let id: Int
let name: String
let members: [Member]
}
struct Member: Codable {
let userID: Int
let lasName, firstName: String
let latitude, longitude: Double
enum CodingKeys: String, CodingKey {
case userID = "userId"
case lasName, firstName, latitude, longitude
}
}
do {
let tr = try JSONDecoder().decode(Root.self,from:jsonData)
print(tr.family.members)
}
catch {
print(error)
}
This question already has an answer here:
Expected to decode Array<Any> but found a dictionary instead
(1 answer)
Closed 4 years ago.
I am developing in Swift 4 and currently using Walmart's API in order to display their products and certain information about the products (example: name of product, price of product). I have read and watched many tutorials regarding parsing JSON data however I continue to get the same error. If anyone could tell me why im getting an error it would be highly appreciated seeing I have been stuck on this issue for days.
Here is the JSON data I am getting from the API call:
{
query: "ipod",
sort: "relevance",
format: "json",
responseGroup: "base",
totalResults: 3570,
start: 1,
numItems: 10,
items: [
{
itemId: 15076191,
parentItemId: 15076191,
name: "Apple iPod Touch 4th Generation 32GB with Bonus Accessory Kit",
salePrice: 189
}
I just want to display the name and salePrice data but I am unable to do so at the moment, instead I get this error: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
Here is my data model:
struct Product: Codable {
let name: String
let salePrice: String
}
Here is the code in my ViewController class:
class ViewController: UIViewController {
import Foundation
import UIKit
var products: [Product]?
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "http://api.walmartlabs.com/v1/search?query=sauce&format=json&apiKey=xyz"
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 productData = try JSONDecoder().decode([Product].self, from: data)
print(productData)
} catch let jsonError {
print(jsonError)
}
}.resume()
}
}
It will be like this,
struct Item: Codable {
let query: String
let sort: String
let responseGroup: String
let totalResults: Int
let start: Int
let numItems: Int
let items: [Product]
}
struct Product: Codable {
let name: String
let salePrice: CGFloat
}
Try using this,
let productData = try JSONDecoder().decode(Item.self, from: data)
Your json data is a dictionary not an array you either parse it and get the array , or try this
struct Item: Codable {
let query: String
let sort: String
let format: String
let responseGroup: String
let totalResults: Int
let start: Int
let numItems: Int
let items: [Product]
}
struct Product: Codable {
let itemId: Double
let parentItemId: Double
let name: String
let salePrice: Int
}
let productData = try JSONDecoder().decode(Item.self, from: data)
I have the json like this:
{"result":0,"data":[{\"ID":7,"TITLE":"123"},{\"ID":8,"TITLE":"123"}]}
I have the struct like this:
struct ResponseResult: Decodable {
let result: Int
let data: [IdResponseResult]
}
struct IdResponseResult: Decodable {
let ID: Int
let TITLE: String
}
So, when I run request like this:
Alamofire.request("https://xxx.xxx.xxx",headers:headers).responseJSON { response in
if let json = response.data {
let decoder = JSONDecoder()
let result = try? decoder.decode(ResponseResult.self, from: json)
print(response.value)
completion(result)
}
}
and print response.value I'm getting this:
data = (
{
"ID" = 7;
TITLE = 123;
},
{
"ID" = 8;
TITLE = 123;
}
);
result = 0;
}
And I cant parse it. How can i resolve it??
the decoding failure is caused by the struct resp2
struct resp2: Decodable {
let ID: Int
let TITLE: String
}
you are defining TITLE:String, but in the JSON you have an Int "TITLE":123.
In case you really want a String (make sense, since is a "Title"), you may need to fix the server side.
edit:
I tried the Decodable as it is now and I am able to recover your structures, you may check with:
let string = "{\"result\": 0,\"data\": [{\"ID\": 7,\"TITLE\": \"123\"}, {\"ID\": 8,\"TITLE\": \"123\"}]}"
let data = string.data(using: .utf8)
do {
let decoder = JSONDecoder()
let result = try? decoder.decode(ResponseResult.self, from: data!)
print(result?.data.first?.TITLE ?? "") // 123
} catch _ {
}
Then I guess there has to be something weird when you receive the data from the service.