Swift 5 (Xcode 11 Betas 5 & 6) - 如何写入 JSON 文件?

Posted

技术标签:

【中文标题】Swift 5 (Xcode 11 Betas 5 & 6) - 如何写入 JSON 文件?【英文标题】:Swift 5 (Xcode 11 Betas 5 & 6) - How to write to a JSON file? 【发布时间】:2019-08-26 22:43:22 【问题描述】:

多年来,这个问题已经被问过很多次了,但它在 Swift 5 中再次发生了变化,尤其是在最近的两个 beta 版本中。

读取 JSON 文件似乎很简单:

func readJSONFileData(_ fileName: String) -> Array<Dictionary<String, Any>> 

    var resultArr: Array<Dictionary<String, Any>> = []

    if let url = Bundle.main.url(forResource: "file", withExtension: "json") 

        if let data = try? Data(contentsOf: url) 

            print("Data raw: ", data)

            if let json = try? (JSONSerialization.jsonObject(with: data, options: []) as! NSArray) 

                print("JSON: ", json)

                if let arr = json as? Array<Any> 

                    print("Array: ", arr)

                    resultArr = arr.map  $0 as! Dictionary<String, Any> 

                

            

        

    

    return resultArr


但是编写起来非常困难,而且在这个网站上找到的所有以前的方法在 Xcode 11 beta 5 和 6 上的 Swift 5 中都失败了。

如何在 Swift 5 中将数据写入 JSON 文件?

我尝试了这些方法:

How to save an array as a json file in Swift? Writing JSON file programmatically swift read/write local json file swift 4

除了弃用警告之外没有任何错误,当我修复这些错误时,它根本不起作用。

【问题讨论】:

它没有改变。 JSONSerializationJSONEncoder 的两个进程都和以往一样。与其向我们展示可以正常工作的无关“阅读”代码,不如向我们展示不适合您的“写作”代码?或者分享一个或两个链接来表示“在这个网站上找到的所有以前的方法都失败了”。 不用说,如果您要使用抛出错误的方法,请使用try 而不是try?,将其包装在do-catch 中,然后打印@ 987654336@ 在catch。错误抛出系统会准确地告诉你出了什么问题。也许您正在尝试保存到捆绑包(只读)。也许您尝试转换的对象具有一些无法在 JSON 中表示的属性/值。在没有reproducible example 的情况下是不可能说的。但是,如果你没有发现错误,那你就瞎了眼。 ***.com/questions/28768015/… ***.com/questions/42550657/… youtube.com/watch?v=IoTt1orlGns forums.developer.apple.com/thread/92806 “假设我有一些 JSON 数据 - 我将如何将其写入 file.json?”一些JSON是什么意思?是字典还是纯文本? @JackBashford 如果您编辑您的问题以显示您的尝试并解释发生了什么而不是简单地链接到其他问题/答案会更好 【参考方案1】:

让我们假设您有一些随机集合(数组或字典或它们的一些嵌套组合):

let dictionary: [String: Any] = ["bar": "qux", "baz": 42]

然后你可以像这样将它保存为 JSON 在“Application Support”目录中:

do 
    let fileURL = try FileManager.default
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        .appendingPathComponent("example.json")

    try JSONSerialization.data(withJSONObject: dictionary)
        .write(to: fileURL)
 catch 
    print(error)

关于我们现在使用“Application Support”目录而不是“Documents”文件夹的原因,请参阅ios Storage Best Practices 视频或参考File System Programming Guide。但是,无论如何,我们使用这些文件夹,而不是应用程序的“bundle”文件夹,它是只读的。

然后读取那个 JSON 文件:

do 
    let fileURL = try FileManager.default
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        .appendingPathComponent("example.json")

    let data = try Data(contentsOf: fileURL)
    let dictionary = try JSONSerialization.jsonObject(with: data)
    print(dictionary)
 catch 
    print(error)


话虽如此,我们通常更喜欢使用强类型的自定义类型,而不是随机字典,因为程序员有责任确保键名中没有拼写错误。无论如何,我们让这些自定义的structclass 类型符合Codable

struct Foo: Codable 
    let bar: String
    let baz: Int

然后我们将使用JSONEncoder 而不是旧的JSONSerialization

let foo = Foo(bar: "qux", baz: 42)
do 
    let fileURL = try FileManager.default
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        .appendingPathComponent("example.json")

    try JSONEncoder().encode(foo)
        .write(to: fileURL)
 catch 
    print(error)

然后读取那个 JSON 文件:

do 
    let fileURL = try FileManager.default
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        .appendingPathComponent("example.json")

    let data = try Data(contentsOf: fileURL)
    let foo = try JSONDecoder().decode(Foo.self, from: data)
    print(foo)
 catch 
    print(error)

有关从自定义类型准备 JSON 的更多信息,请参阅Encoding and Decoding Custom Types 文章或Using JSON with Custom Types 示例代码。

【讨论】:

那么,如果文件不存在,create: true 会创建文件,还是...? 如果 文件夹 可能不存在,请使用 create: true。在应用程序支持目录的情况下,它不会自动创建,除非您将 true 传递给 create 参数。 如果我想在文件不存在的情况下创建文件,我会使用哪个参数列表/方法?抱歉,我只是想了解一下我可以解决的任何潜在问题。 write 方法已经为您创建了文件,如果它不存在的话。 太棒了,谢谢罗伯!我会对其进行测试并回复您。【参考方案2】:

如果有人(像我一样)使用自定义对象并希望使用泛型获得更多“全方位”功能

将文件保存到管理器

func saveJSON<T: Codable>(named: String, object: T) 
    do 
        let fileURL = try FileManager.default
            .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent(named)
        let encoder = try JSONEncoder().encode(object)

        try encoder.write(to: fileURL)
     catch 
        print("JSONSave error of \(error)")
    

从管理器中读取文件

func readJSON<T: Codable>(named: String, _ object: T.Type) -> T? 
     do 
         let fileURL = try FileManager.default
                .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appendingPathComponent(named)
         let data = try Data(contentsOf: fileURL)

         let object = try JSONDecoder().decode(T.self, from: data)

        return object
     catch 
        return nil
    

【讨论】:

【参考方案3】:

回答问题:

假设我有一些 JSON 数据 - 我将如何将其写入 文件.json?它在我的项目目录中 - 这是可写的吗?而如果 不,我怎样才能让它可写?

假设“一些 JSON”表示字典数组为 [&lt;String, String&gt;],下面是一个简单的示例,说明如何做到这一点:

假设我们有以下数组,我们需要将其作为 JSON 写入文件:

let array = [["Greeting":"Hello", "id": "101", "fruit": "banana"],
             ["Greeting":"Hola", "id": "102", "fruit": "tomato"],
             ["Greeting":"Salam", "id": "103", "fruit": "Grape"]]

首先要做的是将其转换为 JSON(作为Data 实例)。还有更多的选择,让我们使用JSONSerialization one:

do 
    let data = try JSONSerialization.data(withJSONObject: array, options: [])
    if let dataString = String(data: data, encoding: .utf8) 
        print(dataString)
    
 catch 
    print(error)

此时,您应该在控制台上看到:

["问候语":"你好","fruit":"banana","id":"101","fruit":"tomato","Greeting":"Hola","id" :"102","fruit":"Grape","Greeting":"Salam","id":"103"]

这是我们格式化为有效 JSON 的数据。

接下来,我们需要将其写入文件。为了做到这一点,我们将使用FileManager 作为:

do 
    let data = try JSONSerialization.data(withJSONObject: array, options: [])
    if let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first 
        let fileUrl = documentDirectoryUrl.appendingPathComponent("MyFile.json")
        try data.write(to: fileUrl)
    
 catch 
    print(error)

注意文件应该存在于documents目录中;在上面的例子中,它的名字应该是“MyFile.json”。

【讨论】:

感谢您的回答艾哈迈德。一个问题 - 什么是文档目录?能否提供一个 Xcode 项目的目录结构,请突出显示目录结构?

以上是关于Swift 5 (Xcode 11 Betas 5 & 6) - 如何写入 JSON 文件?的主要内容,如果未能解决你的问题,请参考以下文章

当我在 Xcode 11 中将 swift 4.2 转换为 swift 5 时继续加载

如何在 Swift 5.2-Xcode 版本 11.5 上覆盖 func observeValueForKeyPath

Xcode 11.1 GM 发布,这可能才是真正的 Xcode 11;Swift 5.2 正式提上日程

如何从 appdelegate xcode 11 swift 5 呈现视图控制器

更新到 Xcode 11 Swift 5.1 后出现 Google AdMob 错误

Xcode 11 Swift 5 - 无法安装应用程序(无法加载 info.plist 文件)