I have searched for many tutorial but the problem still exists :(
First see the JSON file :
{
"header":[
"Academic Year",
"Scheme Type",
"Student Type",
"Institution",
"Deadline",
"Remark"
],
"rows":[
[
"2018-19",
"TSFS",
"Continuing",
"HKU",
"17.Apr.18",
""
],
[
"2018-19",
"TSFS",
"Continuing",
"HKBU",
"23.Apr.18",
""
]
]
}
code:
import UIKit
struct Header:Decodable{
let header:[String]
let row:[Row]
}
struct Row:Codable{
let row:[String]
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "https://api.data.gov.hk/v1/filter?q=%7B%22resource%22%3A%22http%3A%2F%2Fwww.wfsfaa.gov.hk%2Fsfo%2Fpsi%2Fdeadline%2FSFO_Post_Secondary_Schemes_Application_Submission_Deadlines_en.xlsx%22%2C%22section%22%3A1%2C%22format%22%3A%22json%22%7D")
else {return}
//get data
URLSession.shared.dataTask(with: url){
data,response,error in
guard let data = data else { return }
do{
let list = try JSONDecoder().decode(Header.self, from: data)
print(list)
}catch{
print(error)
}
}.resume()
}
}
error:
keyNotFound(CodingKeys(stringValue: "row", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"row\", intValue: nil) (\"row\").", underlyingError: nil))
If I delete “let row:[Row]” I can get the header part of the data:
Header(header: ["Academic Year", "Scheme Type", "Student Type", "Institution", "Deadline", "Remark"])
I think it should be a matter of reading the “rows” line, but I can't find a similar solution,
if someone knows how to solve it, please reply me, thank you very much!!
Inside Header struct row needed to be renamed to rows and the Row struct is no longer needed.
struct Header:Decodable{
let header:[String]
let rows: [[String]]
}
Related
I defined this structure.
import Foundation
import SwiftUI
struct Hoge: Hashable, Codable {
var id: Int
var type: Type
}
enum Type: Codable {
case one
case two
}
And I created json.
[
{
"id": 1,
"type": "one",
},
...
]
And this is decode and load json file.
import Foundation
var hogeList: [Hoge] = load("hoge.json")
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else {
fatalError("Couldnt find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldnt load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldnt parse \(filename) as \(T.self):\n\(error)")
}
}
Crach logs shows this logs.
Fatal error: Couldnt parse hoge.json as Array:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "type", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))
You have two issues in your code:
a) there is an extra comma in your JSON which makes it invalid. Remove the comma after "one"
[
{
"id": 1,
"type": "one"
}
]
b) You need to declare your enumeration type as String:
enum Type: String, Codable {
case one, two
}
Note: I would also rename the enumeration from Type to Kind. You would need to change your json as well or provide a custom CodingKeys to your struct
I'm getting the error "Cannot cast value of type dictionary to array" when I run my code. I'm tryna parse from a json file but its not working. Please help.
Here's the json file and i'm specifically tryna print out just the name values:
{
"data": [
{
"id": "2f4628f7ef6dce33d856121f",
"name": "Top products",
"description": "This category contains all the top products on"
},
{
"id": "3f4628f7ef6dce33d856121d",
"name": "Trending products",
"description": "This category contains all the trending products"
}
],
"page": 0,
"size": 2,
"count": 2,
"status": 200,
"isSuccess": true
}
func fetchFrontPageSections() {
let urlString = APIConstants.baseurl + APIConstants.frontpageSections
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
for dictionary in jsonData as! [[String: AnyObject]] {
print(dictionary["name"])
}
} catch let jsonError {
print(jsonError)
}
}.resume()
}
When I print the dictionary I'm getting that whole son file in my console which is fine but Im expecting to get a result like:
name: Top Products
name: Trending Products
In your constant jsonData you don't have an array, it's a dictionary as your json file explicitly show it. It contains multiple keys such as data, page, size, ...
You shoud access first the value related to the key data that contains an at this point the array of dictionary that you are looking for.
Parsing Json in Swift has never been easier since Codable.
here's an Excellent tutorial from the great guys at Ray Wenderlich: https://www.raywenderlich.com/3418439-encoding-and-decoding-in-swift
for your specific problem try this:
struct Info: Decodable {
let id: String
let name: String
let description: String
}
struct Dictionary: Decodable {
let data: [Info]
let page: Int
let size: Int
let count: Int
let status: Int
let isSuccess: Bool
}
let decoder = JSONDecoder()
let dictionary = try decoder.decode(Dictionary.self, from: json)
I'm trying to parse the JSON at a certain API endpoint using the Codable protocol in Swift. How do you properly decode this?
So far, I've created a Model struct to represent the Post objects located at the endpoint. I'm also using JSONDecoder() to decode the array of Post objects. I've tried restructuring my Model to have nested structs but that doesn't work.
Here's a sample of the JSON:
[
{
"ID": 1,
"title": "Title 1",
"content": "Content 1"
...
},
{
"ID": 2,
"title": "Title 2",
"content": "Content 2"
...
}
]
Here's the Model:
struct Post: Codable {
let id: Int
let title, content: String
enum CodingKeys: String, CodingKey {
case id = "ID"
case title, content
}
}
Here's a sample of the fetching code:
let task = URLSession.shared.dataTask(with: url) {
data, _, error in
if let error = error {
print("Error: Failed to fetch data from url. [\(error.localizedDescription)]")
completion(.failure(error))
return
}
guard let data = data else {
print("Data not available.")
return
}
do {
let decoder = JSONDecoder()
let posts = try decoder.decode([Post].self, from: data)
print("POSTS: : ", posts)
// ^ THIS IS NOT WORKING. I've tried Post.self too.
} catch let jsonError {
//error
}
}
task.resume()
Here's the error I'm getting:
ERROR: keyNotFound(CodingKeys(stringValue: "ID", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"ID\", intValue: nil) (\"ID\").", underlyingError: nil))
Your code is just fine. You just need to check your json data. This error shows in the case:
When certain key in Coding keys doesn't match with the key of json data. In your case it is clearly = "ID"
To solve this check response in postMan and make the keys exactly same in Codingkeys
I'm trying to parse some data from JSON, I already got that working with another API but now I have another struct and I'm getting typeMissmatch Erros...
The JSON looks like:
{
"status": 200,
"data": {
"date": "2018-04-07T00:00:00.508Z",
"featured": [
{
"id": "2345",
"name": "name",
"price": "1,000",
"priceIcon": "String",
"priceIconLink": "URLString",
"images": {
"icon": "URLString",
"png": "URLString",
"gallery": "URLString",
"featured": "URLString"
},
"rarity": "1String",
"type": "1String",
"readableType": "1String"
}
],
"daily": [
{
"id": "12324",
"name": "name",
"price": "1,500",
"priceIcon": "String",
"priceIconLink": "URLString",
"images": {
"icon": "URLString",
"png": "URLString",
"gallery": "URLString",
"featured": "URLString"
},
"rarity": "1String",
"type": "1String",
"readableType": "1String"
}
]
}}
And a Codable struct like that:
struct Base : Codable {
let status : Int
let data : DataItems
}
struct DataItems : Codable {
let date : String
let featured : [Featured]
let daily : [Daily]
}
struct Featured : Codable {
let id : String
let name : String
let price : String
let priceIcon : String
let priceIconLink : String
let images : Images
let rarity : String
let type : String
let readableType : String
}
struct Daily : Codable {
let id : String
let name : String
let price : String
let priceIcon : String
let priceIconLink : String
let images : Images
let rarity : String
let type : String
let readableType : String
}
struct Images : Codable {
let icon : String
let png : String
let gallery : String
let featured : String
}
But when I try to decode that Json I get a "Swift.DecodingError.typeMismatch" Error:
▿ Swift.DecodingError.typeMismatch
▿ typeMismatch: (2 elements)
- .0: Swift.String #0
▿ .1: Swift.DecodingError.Context
▿ codingPath: 5 elements
- CodingKeys(stringValue: "data", intValue: nil)
- CodingKeys(stringValue: "daily", intValue: nil)
▿ _JSONKey(stringValue: "Index 0", intValue: 0)
- stringValue: "Index 0"
▿ intValue: Optional(0)
- some: 0
- CodingKeys(stringValue: "images", intValue: nil)
- CodingKeys(stringValue: "featured", intValue: nil)
- debugDescription: "Expected to decode String but found a number instead."
- underlyingError: nil
My JSON Decoder:
enum Result<Value> {
case success(Value)
case failure(Error)
}
func getItems(for userId: Int, completion: ((Result<Base>) -> Void)?) {
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = "api.jsonbin.io"
urlComponents.path = "/myurl"
let userIdItem = URLQueryItem(name: "userId", value: "\(userId)")
urlComponents.queryItems = [userIdItem]
guard let url = urlComponents.url else { fatalError("Could not create URL from components") }
var request = URLRequest(url: url)
request.httpMethod = "GET"
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"secret-key": "xyz"
]
let session = URLSession(configuration: config)
let task = session.dataTask(with: request) { (responseData, response, responseError) in
DispatchQueue.main.async {
if let error = responseError {
completion?(.failure(error))
} else if let jsonDataTest = responseData {
// Now we have jsonData, Data representation of the JSON returned to us
// from our URLRequest...
// Create an instance of JSONDecoder to decode the JSON data to our
// Codable struct
let decoder = JSONDecoder()
do {
// We would use Post.self for JSON representing a single Post
// object, and [Post].self for JSON representing an array of
// Post objects
let posts = try decoder.decode(Base.self, from: jsonDataTest)
completion?(.success(posts))
} catch {
completion?(.failure(error))
}
} else {
let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
completion?(.failure(error))
}
}
}
task.resume()
}
var base:Base?
func loadJson() {
getItems(for: 1) { (result) in
switch result {
case .success(let base):
self.base = base
dump(base)
case .failure(let error):
fatalError("error: \(error.localizedDescription)")
}
}
}
I'm new to swift and not sure what this Error is telling me or where the problem "decode String but found a number" is. I think there is something wrong with me struct.. I hope someone can help me there.
Please show the code where you want to parse the data to json.
let urlString = "your_url.json"
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 }
do {
//Decode retrived data with JSONDecoder and assing type of Article object
let baseData = try JSONDecoder().decode(Base.self, from: data)
print(baseData) //whole project
print(baseData.status) //200.0
print(baseData.data.date)
for day in baseData.data.daily {
print(day.id)
print(day.images.icon)
print(day.images.featured)
print(day.images.gallery)
print(day.images.png)
print(day.name)
print(day.price)
print(day.priceIcon)
print(day.priceIconLink)
print(day.rarity)
print(day.readableType)
print(day.type)
}
for feature in baseData.data.featured {
print(feature.id)
print(feature.images.icon)
print(feature.images.featured)
print(feature.images.gallery)
print(feature.images.png)
print(feature.name)
print(feature.price)
print(feature.priceIcon)
print(feature.priceIconLink)
print(feature.rarity)
print(feature.readableType)
print(feature.type)
}
} catch let jsonError {
print(jsonError)
}
}.resume()
I tried this and it works for me.
By the way I was a little bit confused that Featured and Daily have all the same variables but are different models.
EDIT
Your posted data in the question are valid. The json from https://api.jsonbin.io/b/5acbd2dc214f9a2b84c6f167/1 is wrong or not consistent.
There is "featured": false in Daily and in Featured it is a string. In the struct is a string expected. So you will get a mismatch. Once you try to parse a string (works) and then you try to parse a boolean to a string (error).
I have this Json:
{ "first": {
"house": [
"small"
]
}, "second": {
"house": [
"small"
] }, "third": {
"car": [
"fast",
"economic"
] }, "fourth": {
"car": [
"fast",
"economic"
] }, "fifth": {
"car": [
"fast",
"economic"
],
"ice": [
"round",
"tasty"
],
"tree": [
"big",
"small"
] } }
I tried to set up a structure with Decodable but I do not get it to work:
struct secondLayer: Codable {
let exchange: [String: [String]]
}
struct decodeJson: Codable {
let symbol: [String: [secondLayer]]
static func decode(jsonString: String) - [decodeJson] {
var output = [decodeJson]()
let decode = JSONDecoder()
do {
let json = jsonString.data(using: .utf8)
output = try! decode.decode([decodeJson].self, from: json!)
} catch {
print(error.localizedDescription)
}
return output
}
}
I get this Error:
Fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.typeMismatch(Swift.Array<Any>,
Swift.DecodingError.Context(codingPath: [], debugDescription:
"Expected to decode Array<Any but found a dictionary instead.",
underlyingError: nil)): file
/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift,
line 181
I tried some modification but I do not get it to work.
The error message
"Expected to decode Array<Any> but found a dictionary instead."
is very clear. You want to decode an array ([decodeJson]) but the root object is a dictionary (starting with {)
Your code cannot work anyway. There are no keys exchange and symbol in the JSON.
Basically there are two ways to decode that JSON:
If all keys are dynamic you cannot decode the JSON to structs. You have to decode it to [String:[String:[String]]]. In this case Codable has no benefit over traditional JSONSerialization.
struct DecodeJson: Codable {
static func decode(jsonString: String) -> [String:[String:[String]]] {
var output = [String:[String:[String]]]()
let decoder = JSONDecoder()
do {
let json = Data(jsonString.utf8)
output = try decoder.decode([String:[String:[String]]].self, from: json)
print(output)
} catch {
print(error.localizedDescription)
}
return output
}
}
Or if the ordinal keys first, second etc are static use an umbrella struct
struct Root : Codable {
let first : [String:[String]]
let second : [String:[String]]
let third : [String:[String]]
let fourth : [String:[String]]
let fifth : [String:[String]]
}
struct DecodeJson {
static func decode(jsonString: String) -> Root? {
let decoder = JSONDecoder()
do {
let json = Data(jsonString.utf8)
let output = try decoder.decode(Root.self, from: json)
return output
} catch {
print(error.localizedDescription)
return nil
}
}
}
Of course you can decode house, car etc into a struct but this requires a custom initializer for each struct because you have to decode a single array manually with unkeyedContainer