将 JSON 保存到 Realm
Saving JSON into Realm
对于我的项目,我想保存从领域中的 API 获取的数据,然后将其显示在 table 视图中。
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
您放置 catch
语句的位置有误——它应该与 do { }
块一致。如果您 format/indent 您的代码(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
}
可能有 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"
}
}
同样,有很多不同的处理方法,所以这是可以扩展的基础知识。
作为建议,Realm Results 是实时更新的对象,也有相应的事件。因此,您可以做的一件巧妙的事情是使结果对象成为您的 tableView 数据源并向其添加一个观察者。
结果对象的工作方式与数组非常相似。
随着领域中书籍的添加、更新或删除,结果对象将反映这些更改,并且将为每本书触发一个事件 - 这使得保持 tableView 更新变得非常简单。
所以在你的 viewController
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
对于我的项目,我想保存从领域中的 API 获取的数据,然后将其显示在 table 视图中。 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
您放置 catch
语句的位置有误——它应该与 do { }
块一致。如果您 format/indent 您的代码(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
}
可能有 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"
}
}
同样,有很多不同的处理方法,所以这是可以扩展的基础知识。
作为建议,Realm Results 是实时更新的对象,也有相应的事件。因此,您可以做的一件巧妙的事情是使结果对象成为您的 tableView 数据源并向其添加一个观察者。
结果对象的工作方式与数组非常相似。
随着领域中书籍的添加、更新或删除,结果对象将反映这些更改,并且将为每本书触发一个事件 - 这使得保持 tableView 更新变得非常简单。
所以在你的 viewController
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