【问题标题】:Saving JSON into Realm将 JSON 保存到领域
【发布时间】:2021-09-24 13:04:14
【问题描述】:

对于我的项目,我想在领域中保存从 API 获取的数据,然后将其显示在表格视图中。 JSON 将如下所示:

{"books":[{"author":"Chinua Achebe", "title":"Things Fall Apart","imageLink":"http://books.google.com/books/content?id=plk_nwEACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api"}]}

我尝试了一些不同的方法,但我不知道如何正确解码和存储 JSON。我之前使用过这个函数来解析 JSON,但是当我添加领域代码时,我得到了错误。

我获取 JSON 的函数是:

func fetchArticle(){

let urlRequest = URLRequest(url: URL(string: "https:/mocki.io/v1/89aa9fe9-fdba-463f-99b3-5d8b6bc1d32e")!)

let task = URLSession.shared.dataTask(with: urlRequest) {  (data,response,error) in
    if error != nil {
        print(error)
        return
    }
    self.books = [Books]()
do {
    let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
    
    if let booksFromJson = json["books"] as? [[String : AnyObject]]{
        for bookFromJson in booksFromJson {
            let book = Books()
            if let title = bookFromJson["title"] as? String, let author = bookFromJson["author"] as? String, let imageLink = bookFromJson["imageLink"] as? String {
                
                book.author = author
                book.title = title
                book.imageLink = imageLink
            }
            self.books?.append(book)
            let realm = try! Realm()
            for books in bookFromJson {
                try! realm.write {
                    realm.add(books, update: .all)
        }
    }
    DispatchQueue.main.async {
        self.tableview.reloadData()
    }
    
} catch let error {
    print(error)
}
}
    task.resume()
}
    }
}

这是我的结构:

class Books: Object, Decodable {
    @objc dynamic var author: String?
    @objc dynamic var imageLink: String?
    @objc dynamic var title: String?
    
convenience init(author: String, imageLink: String, title: String) {
     self.init()
     self.author = author
     self.imageLink = imageLink
     self.title = title
  }

    override static func primaryKey() -> String? {
            return "author"
        }
        
        private enum CodingKeys: String, CodingKey {
            case author
            case imageLink
            case title
}
  }

这是我在函数中遇到的错误:

Invalid conversion from throwing function of type '(Data?, URLResponse?, Error?) throws -> Void' to non-throwing function type '(Data?, URLResponse?, Error?) -> Void'

'let' declarations cannot be computed properties

【问题讨论】:

    标签: swift realm


    【解决方案1】:

    可能有 100 种不同的方法可以将您的 json 映射到领域对象,但让我们保持简单。首先我假设你传入的 json 可能是几本书,所以它看起来像这样

    let jsonStringWithKey = """
        {
            "books":
                [{
                    "author":"Chinua Achebe",
                    "title":"Things Fall Apart",
                    "imageLink":"someLink"
                },
                {
                    "author":"another author",
                    "title":"book title",
                    "imageLink":"another link"
                }]
        }
    """
    

    所以将其编码为数据

    guard let jsonDataWithKey = jsonStringWithKey.data(using: .utf8) else { return }
    

    然后,使用 JSONSerialization 将其映射到一个数组。请记住,顶级对象是“书籍”,而 AnyObject 将是所有子数据

    do {
        if let json = try JSONSerialization.jsonObject(with: jsonDataWithKey) as? [String: AnyObject] {
            if let bookArray = json["books"] as? [[String:AnyObject]] {
                for eachBook in bookArray {
                    let book = Book(withBookDict: eachBook)
                    try! realm.write {
                        realm.add(book)
                    }
                }
            }
        }
    } catch {
        print("Error deserializing JSON: \(error)")
    }
    

    Realm 对象是

    class Book: Object, Codable {
        @objc dynamic var author = ""
        @objc dynamic var title = ""
        @objc dynamic var imageLink = ""
    
        convenience init(withBookDict: [String: Any]) {
            self.init()
    
            self.author = withBookDict["author"] as? String ?? "No Author"
            self.title = withBookDict["title"] as? String ?? "No Title"
            self.imageLink = withBookDict["imageLink"] as? String ?? "No link"
        }
    }
    

    同样,有很多不同的方法来处理这个问题,所以这是可以扩展的基础知识。

    作为建议,领域结果是具有相应事件的实时更新对象。因此,您可以做的一件巧妙的事情是让结果对象成为您的 tableView 数据源并向其添加观察者。

    结果对象的工作方式与数组非常相似。

    当书籍从领域中添加、更新或删除时,结果对象将反映这些更改,并且会为每个更改触发一个事件 - 这使得保持 tableView 更新非常简单。

    所以在你的视图控制器中

    class ViewController: NSViewController {
        var bookResults: Results<PersonClass>? = nil
        @IBOutlet weak var bookTableView: NSTableView!
        var bookToken: NotificationToken?
    
        self.bookResults = realm.objects(Book.self)
    

    然后

    override func viewDidLoad() {
       super.viewDidLoad()
    
       self.bookToken = self.bookResults!.observe { changes in
          //update the tableView when bookResults change
    

    【讨论】:

    • 我真的很想知道为什么不用JSONDecoder
    • @Sulthan 好问题。正如我所提到的,这是一种基础。请记住,有一个顶级关键“书籍”可以使用,并且 JSONDecoder 在接受不是数组或字典的顶级对象时遇到了一些问题。我认为这已解决,但目前没有链接。
    【解决方案2】:

    您的catch 语句的放置位置有误——它应该与do { } 块一致。如果您格式化/缩进代码(Xcode 中的Ctrl-i),这可能更容易查看。

    func fetchArticle(){
        
        let urlRequest = URLRequest(url: URL(string: "https:/mocki.io/v1/89aa9fe9-fdba-463f-99b3-5d8b6bc1d32e")!)
        
        let task = URLSession.shared.dataTask(with: urlRequest) {  (data,response,error) in
            if let error = error {
                print(error)
                return
            }
            self.books = [Books]()
            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
                
                if let booksFromJson = json["books"] as? [[String : AnyObject]]{
                    for bookFromJson in booksFromJson {
                        let book = Books()
                        if let title = bookFromJson["title"] as? String, let author = bookFromJson["author"] as? String, let imageLink = bookFromJson["imageLink"] as? String {
                            
                            book.author = author
                            book.title = title
                            book.imageLink = imageLink
                        }
                        self.books?.append(book)
                        let realm = try! Realm()
                        for books in bookFromJson {
                            try! realm.write {
                                realm.add(books, update: .all)
                            }
                        }
                        DispatchQueue.main.async {
                            self.tableview.reloadData()
                        }
                    }
                }
            } catch { //<-- no need for `let error`
                print(error)
            }
        }
        task.resume() //<-- Moved outside the declaration
    }
    

    【讨论】:

    • 这个答案是否解决了如何将 json 解码为 Realm 对象并保存它们?或者如何更好地处理捕获关闭? (顺便说一句,我同意你的看法,只是想看看这是否也能回答问题)
    • @Jay mine 专门处理 OP 在其初始问题末尾列出的错误。看起来您的问题更具体地处理了领域问题,但没有解决错误。希望两者的结合将有助于OP。如果您想编辑答案以包括语法错误的解释,我很乐意删除我的。
    • 您的回答肯定很有价值!所以不需要改变。他们确实需要正确处理上述错误。我只是想确保您的答案中没有代码更改也解决了保存 Json 数据的问题。赞成您的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    相关资源
    最近更新 更多