これが私がデコードする必要のあるJSONです。その写真とテキストが下のリンクにあります(フォーマットが機能しないため、見栄えが悪くなります)。構造体で正しく表現したと確信しています。
{ "success": { "total": 1 }, "contents": { "quotes": [ { "quote": "The last time doesn't exist. It's only this time. And everything is going to be different this time. There's only now.", "author": "Bill Murray", "length": "118", "tags": [ "inspire", "present" ], "category": "inspire", "title": "Inspiring Quote of the day", "date": "2019-01-16", "id": null } ], "copyright": "2017-19 theysaidso.com" } }
Whenever I run my code, which attempts to take the fields from the JSON and store them into variables in order to display them in a UITableView, it fails. I test it by trying to make a label appear as with the author's name as its title. "author" is a field in the JSON. These are the important parts of the code:
Class ViewController: UITableViewController {
...
var quoteArray = [Quote]()
//quoteArray Stores the quote objects that contain the fields I need
.....
//STRUCTS TO REPRESENT THE JSON
struct Quote: Decodable {
let quote: String?
let author: String?
let length: String?
let tags: [String]?
let category: String?
let title: String?
let date: String?
}
struct WebsiteObjectStruct: Decodable {
let success: SuccessStruct
let contents: ContentsStruct
}
struct SuccessStruct: Decodable{
let total: Int?
}
struct ContentsStruct: Decodable{
let quotes: [Quote]?
let copyright: String?
}
.....
//FUNCTION WHERE DECODING HAPPENS
fileprivate func fetchJSON(){
...
self.websiteObject = try decoder.decode(WebsiteObjectStruct.self, from: data)
self.tableView.reloadData()
...
}
...
//CELL FOR ROW AT FUNCTION FOR TABLEVIEW
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let authorText = quoteArray[0].author
cell.textLabel?.text = author.text
//For quoteArray we are looking at zero index because in my JSON there
// is ONLY EVER ONE element, located at index 0, in quoteArray
return cell
}
}
The app runs and the the tableView is empty, it doesnt have the author's name (In this case bill murray). Anyway, here is the error message:
Failed to decode: typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
It says it expected to decode an array but instead found a dictionary. Well I changed it one time to decode not an array but a struct, and had a variable declared in the class that was of type struct (the purpose of the struct mirrors the purpose of the array).
Short story is, I changed the code slightly to accomodate for the struct, and it could print the author's name to the console ONLY if the print statement was within the same coding brackets as the decoding statement. Still, it couldn't store it into a variable for use.
I don't think the problem is Array vs Dictionary but with the "underlyingError" the console talks about, that the Array is nil. No matter what type the variable is, whether Array or a Struct, the variable getting placed into the textField is always nil.
I get this error:
Fatal Error: unexpected found nil while unwrapping optional value
Perhaps an issue with threading or asynchrosity?
Update: this code works:
class MainNetworkManager{ //Request JSON Format from the wep API
static func fetchJSON(fetchUrl: String, quoteViewController: QuoteViewController) {
let urlString = fetchUrl
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 }
do {
// link in description for video on JSONDecoder
let decoder = JSONDecoder()
// Swift 4.1
decoder.keyDecodingStrategy = .convertFromSnakeCase
//self.webStruct = try decoder.decode(WebsiteObjectStruct.self, from: data)
// self?.quoteArray = quotesArray
// self?.reloadInputViews()
let tempStruct = try decoder.decode(WebsiteObjectStruct.self, from: data)
//print(tempStruct.contents.quotes[0].length)
quoteViewController.webStruct = tempStruct
//quoteViewController.setupLabels(array: (tempStruct.contents.quotes))
quoteViewController.setupLabels(obj: tempStruct)
} catch let jsonErr {
print("Failed to decode:", jsonErr)
}
}
}.resume()
}
It's a little difficult to figure out the mistake with the code you posted. I think the problem could be with the type definition of the variable websiteObject
. I created a a small playground to test it and your structs are fine.
I create a small project that works ok using your structs. You can check it out here: https://github.com/acyrman/StackOverflow54211226.
関連する変更は、fetchJSON関数にあります。self.websiteObject
どのように定義したかわからなかったを使用する代わりにlet websiteObject = try decoder.decode(WebsiteObjectStruct.self, from: data)
、次のようなローカル変数を使用しました。次に、引用符を取得してquoteArray
変数に割り当てます。
fileprivate func fetchJSON() {
let urlString = "http://quotes.rest/qod.json?category=inspire"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
if error != nil {
self?.displayAlert("Error fetching data: \(String(describing: error?.localizedDescription))")
}
let decoder = JSONDecoder()
do {
guard let data = data else { throw NSError(domain: "this.app", code: -1, userInfo: nil) }
let websiteObject = try decoder.decode(WebsiteObjectStruct.self, from: data)
if let quotesArray = websiteObject.contents.quotes {
DispatchQueue.main.async {
self?.quoteArray = quotesArray
self?.tableView.reloadData()
}
}
} catch let error {
self?.displayAlert("Error decoding json data: \(String(describing: error.localizedDescription))")
}
}.resume()
}
アプリの場合、http://quotes.rest/qod.json?category = inspireから見積もりを取得しています。また、info.plistでは、ATS設定を有効にして、https以外のURLからデータをフェッチできるようにすることが重要です。
コードは構造体をテストするためのものであり、クリーンなコードプロジェクトを期待しないでください;)
アプリコールfetchJSON
にviewDidLoad
使用して、subtitle
セルスタイル、このようなUIのルックスを:
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加