将 json 作为字符串保存到 CoreData 并使用字符串创建对象数组

埃文

我正在为一个广播电台创建一个应用程序,我想将“show”对象存储到一个数组中。我使用网络服务器提供 json 数据来填充数组,但我想将此 json 数据作为字符串存储到 CoreData 中,以便对数组的访问不依赖于互联网连接。因此,我想在应用程序启动时更新 CoreData 中的字符串,但基于存储在 CoreData 中的字符串而不是来自网络服务器的 json 数据创建一个数组。

这是我从网络服务器下载 json 数据并将其存储为字符串的函数:

func downloadShows() {
    let urlPath = "http://dogradioappdatabase.com/shows.php"

    guard let url = URL(string: urlPath) else {return}

    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let dataResponse = data,
            error == nil else {
                print(error?.localizedDescription ?? "Response Error")
                return }

        let jsonAsString = self.jsonToString(json: dataResponse)

        DispatchQueue.main.async {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

            let task2 = WebServer(context: context) // Link Task & Context
            task2.showsArray = jsonAsString
            print(jsonAsString)
            (UIApplication.shared.delegate as! AppDelegate).saveContext()
        }
    }
    task.resume()

}    

func jsonToString(json: Data) -> String {
    let convertedString: String

        convertedString = String(data: json, encoding: String.Encoding.utf8)! // the data will be converted to the string
    return convertedString
}

下面是从 CoreData 获取的 json 创建显示数组的函数:

    func createShowsArray () -> Array<ShowModel> {
    var array: Array<ShowModel> = Array()
    var tasks: [WebServer] = []

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    do {
        tasks = try context.fetch(WebServer.fetchRequest())
    }
    catch {
        print("Fetching Failed")
    }

    let arrayAsString: String = tasks[0].showsArray!
    print(arrayAsString)
    do {
        let data1 = arrayAsString.data(using: .utf8)!
        let decoder = JSONDecoder()
        array = try decoder.decode([ShowModel].self, from:
            data1)

    } catch let parsingError {
        print("Error", parsingError)
    }

    return array
}

但是,这不会将数据正确加载到数组中。我在 downloadShows() 函数 (jsonAsString) 中打印了我保存到 CoreData 的值,并将其作为响应:

[{"Name":"Example Show 2","ID":"2","Description":"This ...

但是当我在 createShowsArray() 函数(arrayAsString)中从 CoreData 获取字符串时,它添加了“DOG_Radio.ShowModel”

[DOG_Radio.ShowModel(Name: "Example Show 2", ID: "2", Description: "This ...

JSON 解码器不会将 arrayAsString 解码为实际数组。它抛出这个:

错误 dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data is not valid JSON.",underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 1." UserInfo={NSDebugDescription) =字符 1 周围的值无效。})))

抱歉问了这么长的问题,我只是不知道如何使用 CoreData 将 json 保存为 String 然后将该字符串转换为数组

罗涅洛

将 json 数据或“整个原始数据”存储到 CoreData 中是一种不好的做法。而是将Show其自身存储NSManagedObject. 您可以通过将 JSON 数据转换为对象(看起来您已经在这样做),然后从它们创建 CoreData NSManagedObjects 来完成此操作。

实际上,如果您从 JSON 转换数据没有问题,则无需在保存到 CoreData 之前将其转换为字符串。您可以简单地存储Dataas NSData, ie transformableor binary data,如果您对服务器的提取失败,则稍后将其重新转换。

然而,从长远来看,那不是那么可靠并且更难使用。数据可能已损坏和/或格式错误。

简而言之,您需要一个数据模型和一个 JSON 可读数据结构,您可以将其转换为数据模型以供 CoreData 管理。稍后当您希望允许用户更新、删除、保存或过滤个人时,这将变得很重要Show

Codable将允许您从 JSON 转换为带有JSONDecoder().decode(_:from:).

ShowModelCodeable.swift

import Foundation

struct ShowModelCodeable: Codable {
    var name: String?
    var description: String?
    var producer: String?
    var thumb: String?
    var live: String?
    var banner: String?
    var id: String?

    enum CodingKeys: String, CodingKey {
        case name = "Name"
        case id = "ID"
        case description = "Description"
        case producer = "Producer"
        case thumb = "Thumb"
        case live = "Live"
        case banner = "Banner"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        description = try values.decode(String.self, forKey: .description)
        producer = try values.decode(String.self, forKey: .producer)
        thumb = try values.decode(String.self, forKey: .thumb)
        live = try values.decode(String.self, forKey: .live)
        banner = try values.decode(String.self, forKey: .banner)
        id = try values.decode(String.self, forKey: .id)

    }

    func encode(to encoder: Encoder) throws {

    }
}

接下来,我们需要一个 Core Data Stack 和一个 CoreData 实体。将 Core Data Stack 创建为 Class Singleton 是很常见的,它可以在你的应用程序的任何地方访问。我已经包含了一个基本操作:

核心数据模型的图像

数据库控制器.Swift

import Foundation
import CoreData

class DatabaseController {

    private init() {}

    //Returns the current Persistent Container for CoreData
    class func getContext () -> NSManagedObjectContext {
        return DatabaseController.persistentContainer.viewContext
    }


    static var persistentContainer: NSPersistentContainer = {
        //The container that holds both data model entities
        let container = NSPersistentContainer(name: "StackOverflow")

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */

                //TODO: - Add Error Handling for Core Data

                fatalError("Unresolved error \(error), \(error.userInfo)")
            }


        })
        return container
    }()

    // MARK: - Core Data Saving support
    class func saveContext() {
        let context = self.getContext()
        if context.hasChanges {
            do {
                try context.save()
                print("Data Saved to Context")
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate.
                //You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

    /* Support for GRUD Operations */

    // GET / Fetch / Requests
    class func getAllShows() -> Array<ShowModel> {
        let all = NSFetchRequest<ShowModel>(entityName: "ShowModel")
        var allShows = [ShowModel]()

        do {
            let fetched = try DatabaseController.getContext().fetch(all)
            allShows = fetched
        } catch {
            let nserror = error as NSError
            //TODO: Handle Error
            print(nserror.description)
        }

        return allShows
    }

    // Get Show by uuid
    class func getShowWith(uuid: String) -> ShowModel? {
        let requested = NSFetchRequest<ShowModel>(entityName: "ShowModel")
        requested.predicate = NSPredicate(format: "uuid == %@", uuid)

        do {
            let fetched = try DatabaseController.getContext().fetch(requested)

            //fetched is an array we need to convert it to a single object
            if (fetched.count > 1) {
                //TODO: handle duplicate records
            } else {
                return fetched.first //only use the first object..
            }
        } catch {
            let nserror = error as NSError
            //TODO: Handle error
            print(nserror.description)
        }

        return nil
    }

    // REMOVE / Delete
    class func deleteShow(with uuid: String) -> Bool {
        let success: Bool = true

        let requested = NSFetchRequest<ShowModel>(entityName: "ShowModel")
        requested.predicate = NSPredicate(format: "uuid == %@", uuid)


        do {
            let fetched = try DatabaseController.getContext().fetch(requested)
            for show in fetched {
                DatabaseController.getContext().delete(show)
            }
            return success
        } catch {
            let nserror = error as NSError
            //TODO: Handle Error
            print(nserror.description)
        }

        return !success
    }

}

// Delete ALL SHOWS From CoreData
class func deleteAllShows() {
    do {
        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "ShowModel")
        let deleteALL = NSBatchDeleteRequest(fetchRequest: deleteFetch)

        try DatabaseController.getContext().execute(deleteALL)
        DatabaseController.saveContext()
    } catch {
        print ("There is an error in deleting records")
    }
}

最后,我们需要一种方法来获取 JSON 数据并将其转换为我们的对象,然后显示它。请注意,当按下更新按钮时,它会触发getDataFromServer()这里最重要的一行是

self.newShows = try JSONDecoder().decode([ShowModelCodeable].self, from: dataResponse)

节目正在从您的服务器中拉下,并转换为ShowModelCodeable对象。一旦newShows设置它就会运行 中的代码didSet,在这里您可以删除上下文中的所有对象,然后运行addNewShowsToCoreData(_:)以创建新的 NSManagedObjects 以保存在上下文中。

我已经创建了一个基本的视图控制器并以编程方式添加了一个 tableView 来管理数据。这里Shows是 CoreData 中的 NSManagedObject 数组,newShows是我们从服务器请求中获得的从 json 编码的新对象。

视图控制器.swift

import Foundation
import UIKit
import CoreData

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    // Properties
    var Shows:[ShowModel]?

    var newShows:[ShowModelCodeable]? {
        didSet {
            // Remove all Previous Records
            DatabaseController.deleteAllShows()
            // Add the new spots to Core Data Context
            self.addNewShowsToCoreData(self.newShows!)
            // Save them to Core Data
            DatabaseController.saveContext()
            // Reload the tableView
            self.reloadTableView()
        }
    }

    // Views
    var tableView: UITableView = {
        let v = UITableView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    lazy var updateButton: UIButton = {
        let b = UIButton()
        b.translatesAutoresizingMaskIntoConstraints = false
        b.setTitle("Update", for: .normal)
        b.setTitleColor(.black, for: .normal)
        b.isEnabled = true
        b.addTarget(self, action: #selector(getDataFromServer), for: .touchUpInside)
        return b
    }()

    override func viewWillAppear(_ animated: Bool) {
        self.Shows = DatabaseController.getAllShows()
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.register(ShowCell.self, forCellReuseIdentifier: ShowCell.identifier)

        self.layoutSubViews()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    //TableView -
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return DatabaseController.getAllShows().count
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        // 100
        return ShowCell.height()
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCell(withIdentifier: ShowCell.identifier) as! ShowCell

        self.Shows = DatabaseController.getAllShows()

        if Shows?.count != 0 {
            if let name = Shows?[indexPath.row].name {
                cell.nameLabel.text = name
            }

            if let descriptionInfo = Shows?[indexPath.row].info {
                cell.descriptionLabel.text = descriptionInfo
            }
        } else {
            print("No shows bros")
        }


        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // Show the contents
        print(Shows?[indexPath.row] ?? "No Data For this Row.")

    }

    func reloadTableView() {
        DispatchQueue.main.async {
         self.tableView.reloadData()
        }
    }


    func layoutSubViews() {
        let guide = self.view.safeAreaLayoutGuide
        let spacing: CGFloat = 8

        self.view.addSubview(tableView)
        self.view.addSubview(updateButton)

        updateButton.topAnchor.constraint(equalTo: guide.topAnchor, constant: spacing).isActive = true
        updateButton.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: spacing * 4).isActive = true
        updateButton.rightAnchor.constraint(equalTo: guide.rightAnchor, constant: spacing * -4).isActive = true
        updateButton.heightAnchor.constraint(equalToConstant: 55.0).isActive = true

        tableView.topAnchor.constraint(equalTo: updateButton.bottomAnchor, constant: spacing).isActive = true
        tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: guide.bottomAnchor, constant: spacing).isActive = true
    }


    @objc func getDataFromServer() {
        print("Updating...")
        let urlPath = "http://dogradioappdatabase.com/shows.php"

        guard let url = URL(string: urlPath) else {return}

        let task = URLSession.shared.dataTask(with: url) {
            (data, response, error) in
                guard let dataResponse = data, error == nil else {
                        print(error?.localizedDescription ?? "Response Error")
                return }

            do {
                self.newShows = try JSONDecoder().decode([ShowModelCodeable].self, from: dataResponse)
            } catch {
                print(error)
            }

        }

        task.resume()
    }




    func addNewShowsToCoreData(_ shows: [ShowModelCodeable]) {

        for show in shows {
            let entity = NSEntityDescription.entity(forEntityName: "ShowModel", in: DatabaseController.getContext())
            let newShow = NSManagedObject(entity: entity!, insertInto: DatabaseController.getContext())

            // Create a unique ID for the Show.
            let uuid = UUID()
            // Set the data to the entity
            newShow.setValue(show.name, forKey: "name")
            newShow.setValue(show.description, forKey: "info")
            newShow.setValue(show.producer, forKey: "producer")
            newShow.setValue(show.thumb, forKey: "thumb")
            newShow.setValue(show.live, forKey: "live")
            newShow.setValue(show.banner, forKey: "banner")
            newShow.setValue(show.id, forKey: "id")
            newShow.setValue(uuid.uuidString, forKey: "uuid")
        }

    }



}

查看层次结构

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何将字符串保存到CoreData数组的开头

如何将字符串保存到json文件

使用 Codable 保存到 CoreData 字符串数组

从JSON使用字符串和整数创建数组

将JSON字符串保存到客户端PC(使用HTML5 API)

C#使用字符串数组中的嵌套对象动态创建JSON

将Json数组作为字符串数组访问

将Powershell对象CSV字符串作为文件保存到Azure Blob存储

使用字符串数组创建一个多级对象,将数组解析为JS对象

将 JSON 字符串中的值保存到 Python 中的 .txt 文件

将json字符串保存到txt文件时出现序列化问题

将多行字符串从文本字段保存到JSON文件

如何将哈希或json字符串保存到jsonb字段中

将 json_encode 字符串保存到 mysql db

将JSON字符串保存在对象中

使用 Mongoose 将 ObjectId 作为字符串保存到 MongoDB 失败

使用fetch(url)和json数据具有数组对象,将输出数组作为分隔字符串

使用Laravel 5.6 Blade将PHP数组作为JSON字符串或JSON对象发送到JS Frontend

如何使用改型将JSON作为字符串获取

如何将 JSON 字符串转换为 PHP 对象或数组创建 *code*

如何使用Postman将JSON对象作为JSON字符串发送?

如何将 SwiftyJson 字符串数组保存到字符串数组中

将 JSON 数组字符串化

使用JOLT将JSON字符串数组转换为对象数组

用字符串数组过滤CoreData实体

如何将字符串保存到数组并在标签中显示字符串?

如何将长字符串作为图像保存到SQL Server

Java-将字节数组作为字符串存储在数据库中,并使用字符串值创建字节数组

使用Java将字符串转换为JSON对象