用Swift 4解码解析JSON

时间:2018-04-03 20:28:31

标签: json swift decodable

每次尝试在程序中解析JSON时,我都会收到以下错误。我似乎无法弄明白。

"Expected to decode String but found an array instead.", underlyingError: nil

以下是我一直在努力的代码:

struct Book: Decodable {
    let id: Int
    let title: String
    let chapters: Int
    var pages: [Page]?
}

struct Page: Decodable {
    let id: Int
    let text: [String]
}

struct Chapter: Decodable {
    var chapterNumber: Int
}

func fetchJSON() {
    let urlString = "https://api.myjson.com/bins/kzqh3"
    guard let url = URL(string: urlString) else { return }

    URLSession.shared.dataTask(with: url) { (data, _, err) in
        if let err = err {
            print("Failed to fetch data from", err)
            return
        }
        guard let data = data else { return }
        do {
            let decoder = JSONDecoder()
            let books = try decoder.decode([Book].self, from: data)
            books.forEach({print($0.title)})
        } catch let jsonErr {
            print("Failed to parse json:", jsonErr)
        }
    }.resume()
}

2 个答案:

答案 0 :(得分:1)

您确定这是真正的错误消息吗?

实际上错误应该是

  

"预计会解码String但却找到了字典。"

text的值不是字符串数组,它是一个字典数组

struct Page: Decodable {
    let id: Int
    let text: [[String:String]]
}

不需要结构Chapter

或者编写一个自定义初始化程序,并将包含章节号作为键的字典和作为值的文本解码为Chapter

的数组
struct Book: Decodable {
   let id: Int
   let title: String
   let chapters: Int
   let pages: [Page]
}

struct Page: Decodable {
    let id: Int
    var chapters = [Chapter]()

    private enum CodingKeys : String, CodingKey { case id, chapters = "text" }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        var arrayContainer = try container.nestedUnkeyedContainer(forKey: .chapters)
        while !arrayContainer.isAtEnd {
            let chapterDict = try arrayContainer.decode([String:String].self)
            for (key, value) in chapterDict {
                chapters.append(Chapter(number: Int(key)!, text: value))
            }
        }
    }
}

struct Chapter: Decodable {
    let number : Int
    let text : String
}

答案 1 :(得分:1)

这是有效的:

import UIKit

class ViewController: UIViewController {

 private var books = [Book]()


    struct Book: Decodable {
        let id: Int
        let title: String
        let chapters: Int
        var pages: [Page]
    }

    struct Page: Decodable {
        let id: Int
        let text: [[String:String]]
    }




    override func viewDidLoad() {
        super.viewDidLoad()

        fetchJSON()
    }




    func fetchJSON() {
        let urlString = "https://api.myjson.com/bins/kzqh3"
        guard let url = URL(string: urlString) else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            if error != nil {
                print(error!.localizedDescription)
                return
            }
            guard let data = data else { return }
            do {
                let decoder = JSONDecoder()
                self.books = try decoder.decode([Book].self, from: data)

                DispatchQueue.main.async {

                    for info in self.books {
                        print(info.title)
                        print(info.chapters)
                        print(info.pages[0].id)
                        print(info.pages[0].text)
                        print("-------------------")
                    }

                }

            } catch let jsonErr {
                print("something wrong after downloaded: \(jsonErr) ")
            }
            }.resume()
    }

}

// print
//Genesis
//50
//1
//[["1": "In the beg...]]
//-------------------
//Exodus
//40
//2
//[["1": "In the beginning God created...]]
//

如果你需要打印书中每章的价值,我可以使用这样的东西:

import UIKit

class ViewController: UIViewController {

 private var books = [Book]()


    struct Book: Decodable {
        let id: Int
        let title: String
        let chapters: Int
        var pages: [Page]
    }

    struct Page: Decodable {
        let id: Int
        let text: [[String:String]]
    }




    override func viewDidLoad() {
        super.viewDidLoad()

        fetchJSON()
    }




    func fetchJSON() {
        let urlString = "https://api.myjson.com/bins/kzqh3"
        guard let url = URL(string: urlString) else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            if error != nil {
                print(error!.localizedDescription)
                return
            }
            guard let data = data else { return }
            do {
                let decoder = JSONDecoder()
                self.books = try decoder.decode([Book].self, from: data)

                DispatchQueue.main.async {

                    for info in self.books {
                        print(info.title)
                        print(info.chapters)
                        print(info.pages[0].id)
                        //print(info.pages[0].text)

                        for cc in info.pages[0].text {

                            for (key, value) in cc {
                                print("\(key) : \(value)")
                            }
                        }

                        print("-------------------")
                    }

                }

            } catch let jsonErr {
                print("something wrong after downloaded: \(jsonErr) ")
            }
            }.resume()
    }

}


//Genesis
//50
//1
//1 : In the beginning God ...
//2 : But the earth became waste...
//.
//.
//.
//31 : And God saw everything...
//-------------------
//Exodus
//40
//2
//1 : In the beginning God...
//2 : But the earth became...
//.
//.
//.
//31 : And God saw everything