【问题标题】:How to save an array as a json file in Swift?如何在 Swift 中将数组保存为 json 文件?
【发布时间】:2015-02-27 14:59:47
【问题描述】:

我是 swift 的新手,但遇到了麻烦。所以我需要做的是将此数组保存为 json 文件在 iphone 的文件夹中。

var levels = ["unlocked", "locked", "locked"]

然后稍后能够将其读回另一个数组。有人可以告诉我该怎么做吗?或提供完成此操作的确切代码。

编辑: 我发现了一个例子。他们是这样设置数据的:

 "[ {"person": {"name":"Dani","age":"24"}}, {"person": {"name":"ray","age":"70"}} ]" 

你可以通过这种方式访问​​它:

 if let item = json[0] 
   { if let person = item["person"] 
     { if let age = person["age"] 
      { println(age) } } }

但我需要能够执行相同的操作,但需要使用保存在文档文件夹中的文件。

【问题讨论】:

  • 在我们给您“完成此任务的确切代码”之前,您能否提供您对此的研究结果?
  • 我到处找,但找不到类似的东西。例如,我看到的所有示例代码都以这种方式提供数据 "[ {"person": {"name":"Dani","age":"24"}}, {"person": {"name":" ray","age":"70"}} ]" 然后提到你可以访问这样的信息 "if let item = json[0] { if let person = item["person"] { if let age = person["age"] { println(age) } } }" 这比我需要做的复杂一点,因为我只有一个简单的数组。我还没有看到他们将数据保存到 json 文件到文件夹中的任何示例
  • 显示您研究过的内容有助于我们了解您的知识,从而为您提供最佳答案。

标签: json xcode swift


【解决方案1】:

如果你像我一样不喜欢使用全新的第三方框架来完成这样的小事,这里是我在 vanilla Swift 中的解决方案。从在 Documents 文件夹中创建 .json 文件到将 JSON 写入其中。

let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!

let jsonFilePath = documentsDirectoryPath.URLByAppendingPathComponent("test.json")
let fileManager = NSFileManager.defaultManager()
var isDirectory: ObjCBool = false

// creating a .json file in the Documents folder
if !fileManager.fileExistsAtPath(jsonFilePath.absoluteString, isDirectory: &isDirectory) {
    let created = fileManager.createFileAtPath(jsonFilePath.absoluteString, contents: nil, attributes: nil)
    if created {
        print("File created ")
    } else {
        print("Couldn't create file for some reason")
    }
} else {
    print("File already exists")
}

// creating an array of test data
var numbers = [String]()
for var i = 0; i < 100; i++ {
    numbers.append("Test\(i)")
}

// creating JSON out of the above array
var jsonData: NSData!
do {
    jsonData = try NSJSONSerialization.dataWithJSONObject(numbers, options: NSJSONWritingOptions())
    let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)
    print(jsonString)
} catch let error as NSError {
    print("Array to JSON conversion failed: \(error.localizedDescription)")
}

// Write that JSON to the file created earlier
let jsonFilePath = documentsDirectoryPath.URLByAppendingPathComponent("test.json")
do {
    let file = try NSFileHandle(forWritingToURL: jsonFilePath)
    file.writeData(jsonData)
    print("JSON data was written to teh file successfully!")
} catch let error as NSError {
    print("Couldn't write to file: \(error.localizedDescription)")
}

【讨论】:

  • 在 Swift 4.x 项目中使用此功能的任何人都必须使用 jsonFilePath.path 而不是 jsonFilePath.absoluteString
【解决方案2】:

#1。将 Swift Array 保存为 json 文件

以下 Swift 3 / iOS 10 代码展示了如何将 Array 实例转换为 json 数据,并使用 FileManagerJSONSerialization 将其保存到位于 iPhone 文档目录中的 json 文件中:

func saveToJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentDirectoryUrl.appendingPathComponent("Persons.json")

    let personArray =  [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]

    // Transform array into data and save it into file
    do {
        let data = try JSONSerialization.data(withJSONObject: personArray, options: [])
        try data.write(to: fileUrl, options: [])
    } catch {
        print(error)
    }
}

/*
 Content of Persons.json file after operation:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

作为替代方案,您可以实现以下使用流的代码:

func saveToJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentDirectoryUrl.appendingPathComponent("Persons.json")

    let personArray =  [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]

    // Create a write-only stream
    guard let stream = OutputStream(toFileAtPath: fileUrl.path, append: false) else { return }
    stream.open()
    defer {
        stream.close()
    }

    // Transform array into data and save it into file
    var error: NSError?
    JSONSerialization.writeJSONObject(personArray, to: stream, options: [], error: &error)

    // Handle error
    if let error = error {
        print(error)
    }
}

/*
 Content of Persons.json file after operation:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

#2。从 json 文件中获取 Swift Array

以下 Swift 3 / iOS 10 代码展示了如何从位于 iPhone 文档目录中的 json 文件中获取数据,并使用 FileManagerJSONSerialization 将其转换为 Array 实例:

/*
 Content of Persons.json file:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

func retrieveFromJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentsDirectoryUrl.appendingPathComponent("Persons.json")

    // Read data from .json file and transform data into an array
    do {
        let data = try Data(contentsOf: fileUrl, options: [])
        guard let personArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: [String: String]]] else { return }
        print(personArray) // prints [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]
    } catch {
        print(error)
    }
}

作为替代方案,您可以实现以下使用流的代码:

/*
 Content of Persons.json file:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

func retrieveFromJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentsDirectoryUrl.appendingPathComponent("Persons.json")

    // Create a read-only stream
    guard let stream = InputStream(url: fileUrl) else { return }
    stream.open()
    defer {
        stream.close()
    }

    // Read data from .json file and transform data into an array
    do {
        guard let personArray = try JSONSerialization.jsonObject(with: stream, options: []) as? [[String: [String: String]]] else { return }
        print(personArray) // prints [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]
    } catch {
        print(error)
    }
}

位于 Github 的 Save-and-read-JSON-from-Playground 存储库中的 Playground 展示了如何将 Swift Array 保存到 json 文件中,以及如何读取 json 文件并从中获取 Swift Array

【讨论】:

  • JSONSerialization 有读写流的方法,为什么要手动呢?
  • @gasher729 我已经用适当的JSONSerialization 处理流的方法更新了我的代码。谢谢。
  • 这是唯一对我有用的解决方案。从上面将 dict 解析为字符串的另一种方法在稍后从文件中读取时给了我错误“格式错误”。
【解决方案3】:

在 Swift 4 中,这已经内置于 JSONEncoder

let pathDirectory = getDocumentsDirectory()
try? FileManager().createDirectory(at: pathDirectory, withIntermediateDirectories: true)
let filePath = pathDirectory.appendingPathComponent("levels.json")

let levels = ["unlocked", "locked", "locked"]
let json = try? JSONEncoder().encode(levels)

do {
     try json!.write(to: filePath)
} catch {
    print("Failed to write JSON data: \(error.localizedDescription)")
}

func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

您尝试编码的对象必须符合Encodable 协议。

阅读Apple's official guide,了解如何将现有对象扩展为可编码。

【讨论】:

  • 嘿@Mastergalen,很棒的答案。我能问一下 withIntermediateDirectories 是做什么的吗?我找不到任何文档。
  • @CristianMoisei 如果它们不存在,它将创建所有嵌套目录。例如,与在 mkdir 命令中添加 -p 标志相同。
  • 现在对 Swift 进行了增强,这应该是开发人员使用的解决方案。
【解决方案4】:

我推荐你使用SwiftyJSON 框架。研究它的文档并学习如何将字符串写入文件(提示:NSFileHandle

类似于下面的代码,但您确实需要同时研究 SwiftyJSON 和 NSFileHandle 以了解如何将 JSON 数据序列化为文件并从文件中解析 JSON 数据

let levels = ["unlocked", "locked", "locked"]
let json = JSON(levels)
let str = json.description
let data = str.dataUsingEncoding(NSUTF8StringEncoding)!
if let file = NSFileHandle(forWritingAtPath:path) {
    file.writeData(data)
} 

【讨论】:

  • Chris Lattner + Swift 编译器团队的其他成员建议不要使用任何第三方框架。
  • 为什么要使用第三方框架来完成像 JSON 序列化这样简单的事情?它会不必要地膨胀您的代码,并使您面临被 Apple 拒绝的风险。
  • 这是 Codable 协议发布之前的旧答案。苹果不会因此而拒绝。
【解决方案5】:

这里是通用的 Swift 解决方案

我创建了通用类,可以轻松做到这一点

//
//  OfflineManager.swift
// 
//
//  Created by Prashant on 01/05/18.
//  Copyright © 2018 Prashant. All rights reserved.
//

import UIKit

class OfflineManager: NSObject {

    static let sharedManager = OfflineManager()
    let LocalServiceCacheDownloadDir        = "LocalData"

    // Replace case as your naming 

    enum WSCacheKeys {
        case CampignList . 
        case CampignDetail(id:String)
        case ScreenShotList

        var value:String {
            switch self {
            case .CampignList:
              return  "CampignList"
            case .CampignDetail(let id):
                return id
            case .ScreenShotList :
                return "ScreenShotList"
            }

        }
    }

    func getBaseForCacheLocal(with fileName:String) -> String? {

        let filePath = FileManager.default.getDocumentPath(forItemName: self.LocalServiceCacheDownloadDir)
        if FileManager.default.directoryExists(atPath: filePath) {
            return filePath.stringByAppendingPathComponent(fileName)
        } else {
            if  FileManager.default.createDirectory(withFolderName: self.LocalServiceCacheDownloadDir) {
                return filePath.stringByAppendingPathComponent(fileName)
            }
        }
        return nil
    }



    //------------------------------------------------------------

    @discardableResult
    func cacheDataToLocal<T>(with Object:T,to key:WSCacheKeys) -> Bool {
        let success = NSKeyedArchiver.archiveRootObject(Object, toFile: getBaseForCacheLocal(with: key.value)!)
        if success {
            print( "Local Data Cached\(String(describing: getBaseForCacheLocal(with: key.value)))")
        } else {
            print("Error")
        }

        return success

    }

    //------------------------------------------------------------

    func loadCachedDataFromLocal<T>(with key:WSCacheKeys ) -> T? {
        return NSKeyedUnarchiver.unarchiveObject(withFile: getBaseForCacheLocal(with: key.value)!) as? T
    }


    //------------------------------------------------------------


    func removeAllCacheDirs () {
        do {
            try FileManager.default.removeItem(atPath: self.getBaseForCacheLocal(with: "")!)

        } catch {
            print("error in remove dir \(error.localizedDescription)")
        }

    }

    //--------------------------------------------------------------------------------


}

这里是extension FileManager的一些辅助方法

public var getDocumentDirectoryPath: String {
    let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    return documentDirectory
}

public func getDocumentPath(forItemName name: String)-> String {
    return getDocumentDirectoryPath.stringByAppendingPathComponent(name)
}

public func directoryExists(atPath filePath: String)-> Bool {
    var isDir = ObjCBool(true)
    return FileManager.default.fileExists(atPath: filePath, isDirectory: &isDir )
}

public func createDirectory(withFolderName name: String)-> Bool {
    let finalPath = getDocumentDirectoryPath.stringByAppendingPathComponent(name)
    return createDirectory(atPath: finalPath)
}

这是字符串扩展的方法

public func stringByAppendingPathComponent(_ path: String) -> String {
    let fileUrl = URL.init(fileURLWithPath: self)
    let filePath = fileUrl.appendingPathComponent(path).path
    return filePath
}

怎么用?

保存

   OfflineManager.sharedManager.cacheDataToLocal(with: object as! [String:Any], to: .CampignList)

读取数据

    DispatchQueue.global().async {
        // GET OFFLINE DATA
        if let object:[String:Any] = OfflineManager.sharedManager.loadCachedDataFromLocal(with: .CampignList) {
            do {
                let data = try  JSONSerialization.data(withJSONObject: object, options: [])
                let object = try CampaignListResponse.init(data: data)
                self.arrCampignList = object.data ?? []
                DispatchQueue.main.async {
                    self.tableVIew.reloadData()
                }
            } catch {
            }
        }
      }

注意:您可以为您的 json 类型定义自己的 WSCacheKeys,就像我正在获取一些广告系列列表一样

【讨论】:

    【解决方案6】:

    这是 Isuru 在 Swift 4.2 中的回答。这在操场上有效:

    let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
    let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
    
    let jsonFilePath = documentsDirectoryPath.appendingPathComponent("test.json")
    let fileManager = FileManager.default
    var isDirectory: ObjCBool = false
    
    // creating a .json file in the Documents folder
    if !fileManager.fileExists(atPath: (jsonFilePath?.absoluteString)!, isDirectory: &isDirectory) {
        let created = fileManager.createFile(atPath: jsonFilePath!.absoluteString, contents: nil, attributes: nil)
        if created {
            print("File created ")
        } else {
            print("Couldn't create file for some reason")
        }
    } else {
        print("File already exists")
    }
    
    // creating an array of test data
    var numbers = [String]()
    for i in 0..<100 {
        numbers.append("Test\(i)")
    }
    
    // creating JSON out of the above array
    var jsonData: NSData!
    do {
        jsonData = try JSONSerialization.data(withJSONObject: numbers, options: JSONSerialization.WritingOptions()) as NSData
        let jsonString = String(data: jsonData as Data, encoding: String.Encoding.utf8)
        print(jsonString as Any)
    } catch let error as NSError {
        print("Array to JSON conversion failed: \(error.localizedDescription)")
    }
    
    // Write that JSON to the file created earlier
    //    let jsonFilePath = documentsDirectoryPath.appendingPathComponent("test.json")
    do {
        let file = try FileHandle(forWritingTo: jsonFilePath!)
        file.write(jsonData as Data)
        print("JSON data was written to teh file successfully!")
    } catch let error as NSError {
        print("Couldn't write to file: \(error.localizedDescription)")
    }
    

    【讨论】:

      猜你喜欢
      • 2021-01-21
      • 1970-01-01
      • 1970-01-01
      • 2015-12-11
      • 2019-02-13
      • 2021-03-19
      • 1970-01-01
      • 2019-01-05
      • 2017-06-24
      相关资源
      最近更新 更多