I'm parsing through JSON on a website like so (under a request.httpMethod = "GET" in Swift
):
let example = json.data.first?.TShirtPrice
The JSON I'm getting is structured like so
{"returned":1,"data":[{"TShirtPrice":"5"}]}
But I have a new JSON set that is structured without [] brackets like so:
{"returned":1,"base":"USD","data":{"TShirtPrice":"3.448500"}}
The same exact code doesn't let me get the price of the shirt anymore -- what is the fix? Thank you!
This is my code
if let data = data {
do {
let json = try JSONDecoder().decode(Root.self,from: data)
let price = json.data.first?.TShirtPrice
struct Root: Codable {
let data: [Datum]
}
struct Datum: Codable {
let TShirtPrice: String
}
Assuming your data model is something as follows You can be using Struct or Class it's not an issue.
struct Root: Decodable {
let returned: Int?
let base: String?
let data: Price?
}
struct Price: Codable {
let TShirtPrice: String?
}
Sample JSON Sting is as follows
let jsonString = """
{
"returned": 1,
"base": "USD",
"data": {
"TShirtPrice": "3.448500"
}
}
"""
You just have to change the way data is parsed by making change in data model as given above and way to access the data as given below
if let data = jsonString.data(using: .utf8) {
let myObject = try JSONDecoder().decode(Root.self, from: data)
print(myObject.data?.TShirtPrice)
}
In your case it will look like this
if let data = data {
do {
let json = try JSONDecoder().decode(Root.self,from: data)
let Price = json.data?.TShirtPrice
}
}
What is changed here?
As your price data was in format of Array the code was written accordingly and as per new data it's not an Array anymore so you have to adapt those changes app side also.
Related
I am trying to write a Swift Codable model for the below JSON.
{
"batchcomplete": "",
"query": {
"pages": {
"26667" (The problem is here): {
"pageid": 26667,
"ns": 0,
"title": "Spain",
"contentmodel": "wikitext",
"pagelanguage": "en",
"pagelanguagehtmlcode": "en",
"pagelanguagedir": "ltr",
"touched": "2020-03-14T18:03:48Z",
"lastrevid": 945549863,
"length": 254911,
"fullurl": "https://en.wikipedia.org/wiki/Spain",
"editurl": "https://en.wikipedia.org/w/index.php?title=Spain&action=edit",
"canonicalurl": "https://en.wikipedia.org/wiki/Spain"
}
}
}
}
The problem is that one of the key changes every time I query.
Marked the in the above JSON as (The problem is here)
How to parse the above JSON file with JSONDecoder?
This can be easily parsed with libraries like SwiftyJSON.
The point is in making let pages: [String:Item] Use
// MARK: - Root
struct Root: Codable {
let batchcomplete: String
let query: Query
}
// MARK: - Query
struct Query: Codable {
let pages: [String:Item]
}
// MARK: - Item
struct Item: Codable {
let pageid, ns: Int
let title, contentmodel, pagelanguage, pagelanguagehtmlcode: String
let pagelanguagedir: String
let touched: Date
let lastrevid, length: Int
let fullurl: String
let editurl: String
let canonicalurl: String
}
let res = try JSONDecoder().decode(Root.self, from: data)
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)
}
All my JSON responses follow the same structure:
"success": <http code>,
"data": [
]
Where the data sent back can vary. Sometimes it can contain Users, sometimes Comments, etc. So I want to create a Codable struct that is flexible to handle the various types of objects being sent back in the data array.
Here is my current struct:
struct BasicResponse: Codable {
let success: Int
let data: [User]
}
As you can see, it currently only handles User data being sent back.
Then, I read the JSON data like this (through Alamofire/Moya):
var users = [User]()
let results = try JSONDecoder().decode(BasicResponse.self, from: response.data)
self.users.append(contentsOf: results.data)
How can I change my struct file to be more flexible, and how would I then cast the JSON response to the desired object?
So, without going through a lot of design cycles and straight off the top my head, I'd consider trying Swift's generic support, for example...
struct BasicResponse<DataType>: Codable where DataType: Codable {
let success: Int
let data: [DataType]
}
Then you just need to define the implementation of DataTypes you want to use
struct User: Codable {
var name: String
}
And decode it...
let decoder = JSONDecoder()
let response = try decoder.decode(BasicResponse<User>.self, from: data)
print(response.data[0].name)
Now, I just threw this into a Playground and tested it with some basic data...
struct User: Codable {
var name: String
}
struct BasicResponse<T>: Codable where T: Codable {
let success: Int
let data: [T]
}
let data = "{\"success\": 200, \"data\": [ { \"name\":\"hello\" }]}".data(using: .utf8)!
let decoder = JSONDecoder()
do {
let response = try decoder.decode(BasicResponse<User>.self, from: data)
response.data[0].name
} catch let error {
print(error)
}
You might need to "massage" the design to better meet your needs, but it might give you a place to start
I am trying to validated students == null or values avilable, If values avilable I need to get grade and store grade into table data array and subject null also I need to store in same array For example: [10, null, 11] from below JSON. how to append like this in single array from JSON response.
{
"students":[
{
"id":0,
"subject":[
{
"grade":10
}
]
},
{
"id":1,
"subject":null
},
{
"id":2,
"subject":[
{
"grade":11
}
]
}
]
}
Expected output: [10,null,11,......] //This array I am going to use Tableview cell
I am validating based on null and not null array values within cell for row. I can use var array = [String?] for accepting null values but how to append two different field result into same array?
You should take a look into the 'Codable' protocol.
By simply defining a struct like:
struct Student: Codable
you can decode it from JSON into these objects.
See for example: hackernoon or grokswift
This looks like a trivial scenario. Best solution is Decodable. Your payload loaded from network or whatever will be parsed into structure. Now you can easily make any manipulations.
Setup: Open a new project. Add "payload.json" file with json payload you provided in question.
Add the following to your project.
import UIKit
struct StudentData: Decodable {
var students: [Student]
}
struct Student: Decodable {
var id: Int
var subject: [Subject]?
}
struct Subject: Decodable {
var grade: Int
}
class ViewController: UIViewController {
var data: Data? {
guard let path = Bundle(for: type(of: self)).path(forResource: "payload", ofType: "json") else { return nil }
return try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
}
override func viewDidLoad() {
super.viewDidLoad()
if let data = data {
do {
let studentData = try JSONDecoder().decode(StudentData.self, from: data)
print(studentData)
// manipulate the structure in any way you want
let subjects: [Subject?] = studentData.students.map { $0.subject?.first }
print(subjects)
let nonNilValues = subjects.compactMap { $0 }
print(subjects)
// ... etc
} catch let error {
print(error.localizedDescription)
}
}
}
}
Sorry for not coding in playgrounds. It's way too buggy.
Try this
let students = [["id": 0,"subject": [["grade": 10]]],
["id": 0,"subject": nil],
["id": 0,"subject": [["grade": 10]]]] as! [Dictionary<String,Any>]
let array = students.map({(($0["subject"] as? [Any])?.first as? Dictionary<String,Int>)?["grade"]})
print(array)
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.