使用Swift读取JSON文件

Kri*_*enz 174 xcode json nsjsonserialization swift ios8

我真的很想尝试将一个JSON文件读入Swift,所以我可以玩它.我已经花了2天的时间重新搜索和尝试不同的方法,但没有运气,所以我已经注册StackOverFlow,看看是否有人可以指出我正确的方向.....

我的JSON文件名为test.json,包含以下内容:

{
  "person":[
     {
       "name": "Bob",
       "age": "16",
       "employed": "No"
     },
     {
       "name": "Vinny",
       "age": "56",
       "employed": "Yes"
     }
  ]
}    
Run Code Online (Sandbox Code Playgroud)

该文件直接存储在文档中,我使用以下代码访问它:

let file = "test.json"
let dirs : String[] = NSSearchPathForDirectoriesInDomains(
                                                          NSSearchpathDirectory.DocumentDirectory,
                                                          NSSearchPathDomainMask.AllDomainMask,
                                                          true) as String[]

if (dirs != nil) {
    let directories: String[] = dirs
    let dir = directories[0]
    let path = dir.stringByAppendingPathComponent(file)
}

var jsonData = NSData(contentsOfFile:path, options: nil, error: nil)
println("jsonData \(jsonData)" // This prints what looks to be JSON encoded data.

var jsonDict = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: nil) as? NSDictionary

println("jsonDict \(jsonDict)") - This prints nil..... 
Run Code Online (Sandbox Code Playgroud)

如果有人能够在正确的方向上推动我如何反序列化JSON文件并将其放在一个可访问的Swift对象中,我将永远感激不尽!

亲切的问候,

Krivvenz.

Abh*_*hek 260

请遵循以下代码:

if let path = NSBundle.mainBundle().pathForResource("test", ofType: "json")
{
    if let jsonData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)
    {
        if let jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary
        {
            if let persons : NSArray = jsonResult["person"] as? NSArray
            {
                // Do stuff
            }
        }
     }
}
Run Code Online (Sandbox Code Playgroud)

数组"人员"将包含关键人物的所有数据.迭代通过获取它.

Swift 4.0:

if let path = Bundle.main.path(forResource: "test", ofType: "json") {
    do {
          let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
          let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
          if let jsonResult = jsonResult as? Dictionary<String, AnyObject>, let person = jsonResult["person"] as? [Any] {
                    // do stuff
          }
      } catch {
           // handle error
      }
}
Run Code Online (Sandbox Code Playgroud)

  • "让jsonData = NSData(contentsOfFile:path!)"而不是"let jsonData = NSData.dataWithContentsOfFile(path,options:.DataReadingMappedIfSafe,error:nil)" (14认同)
  • 最好在这里使用guard else语句而不是这个厄运金字塔. (4认同)
  • 不要忘记将该文件添加到您的包中。/sf/ask/2924289441/ (4认同)
  • 如果您解释了为什么/如何解决问题中所描述的问题,而不是仅仅提供一堆代码,那将更有帮助。 (3认同)

Aks*_*Aks 129

如果有人在寻找SwiftyJSON答案:
更新:
对于Swift 3/4:

if let path = Bundle.main.path(forResource: "assets/test", ofType: "json") {
    do {
        let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
        let jsonObj = try JSON(data: data)
        print("jsonData:\(jsonObj)")
    } catch let error {
        print("parse error: \(error.localizedDescription)")
    }
} else {
    print("Invalid filename/path.")
}
Run Code Online (Sandbox Code Playgroud)

  • 这让我变成了swiftyJSON和carthage!谢谢 :) (2认同)

yar*_*rlg 84

Swift 4使用Decodable

struct ResponseData: Decodable {
    var person: [Person]
}
struct Person : Decodable {
    var name: String
    var age: String
    var employed: String
}

func loadJson(filename fileName: String) -> [Person]? {
    if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
        do {
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode(ResponseData.self, from: data)
            return jsonData.person
        } catch {
            print("error:\(error)")
        }
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特3

func loadJson(filename fileName: String) -> [String: AnyObject]? {
    if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
        do {
            let data = try Data(contentsOf: url)
            let object = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
            if let dictionary = object as? [String: AnyObject] {
                return dictionary
            }
        } catch {
            print("Error!! Unable to parse  \(fileName).json")
        }
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该移动到新的文档功能,或标记为正确的答案. (8认同)

Gon*_*zer 29

斯威夫特 5.1,Xcode 11

你可以使用这个:


struct Person : Codable {
    let name: String
    let lastName: String
    let age: Int
}

func loadJson(fileName: String) -> Person? {
   let decoder = JSONDecoder()
   guard
        let url = Bundle.main.url(forResource: fileName, withExtension: "json"),
        let data = try? Data(contentsOf: url),
        let person = try? decoder.decode(Person.self, from: data)
   else {
        return nil
   }

   return person
}
Run Code Online (Sandbox Code Playgroud)


ben*_*ben 24

Xcode 8 Swift 3从文件更新中读取json:

    if let path = Bundle.main.path(forResource: "userDatabseFakeData", ofType: "json") {
        do {
            let jsonData = try NSData(contentsOfFile: path, options: NSData.ReadingOptions.mappedIfSafe)
            do {
                let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
                if let people : [NSDictionary] = jsonResult["person"] as? [NSDictionary] {
                    for person: NSDictionary in people {
                        for (name,value) in person {
                            print("\(name) , \(value)")
                        }
                    }
                }
            } catch {}
        } catch {}
    }
Run Code Online (Sandbox Code Playgroud)


Nic*_*ham 14

更新了Swift 3.0的名称

根据Abhishek的回答Druva的回答

func loadJson(forFilename fileName: String) -> NSDictionary? {

    if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
        if let data = NSData(contentsOf: url) {
            do {
                let dictionary = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as? NSDictionary

                return dictionary
            } catch {
                print("Error!! Unable to parse  \(fileName).json")
            }
        }
        print("Error!! Unable to load  \(fileName).json")
    }

    return nil
}
Run Code Online (Sandbox Code Playgroud)


kjm*_*kjm 10

Swift 2.1回答(基于Abhishek的):

    if let path = NSBundle.mainBundle().pathForResource("test", ofType: "json") {
        do {
            let jsonData = try NSData(contentsOfFile: path, options: NSDataReadingOptions.DataReadingMappedIfSafe)
            do {
                let jsonResult: NSDictionary = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
                if let people : [NSDictionary] = jsonResult["person"] as? [NSDictionary] {
                    for person: NSDictionary in people {
                        for (name,value) in person {
                            print("\(name) , \(value)")
                        }
                    }
                }
            } catch {}
        } catch {}
    }
Run Code Online (Sandbox Code Playgroud)


Ash*_*k R 10

Swift 3.0,Xcode 8,iOS 10

 if let path = Bundle.main.url(forResource: "person", withExtension: "json") {

        do {
            let jsonData = try Data(contentsOf: path, options: .mappedIfSafe)
            do {
                if let jsonResult = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions(rawValue: 0)) as? NSDictionary {
                    if let personArray = jsonResult.value(forKey: "person") as? NSArray {
                        for (_, element) in personArray.enumerated() {
                            if let element = element as? NSDictionary {
                                let name = element.value(forKey: "name") as! String
                                let age = element.value(forKey: "age") as! String
                                let employed = element.value(forKey: "employed") as! String
                                print("Name: \(name),  age: \(age), employed: \(employed)")
                            }
                        }
                    }
                }
            } catch let error as NSError {
                print("Error: \(error)")
            }
        } catch let error as NSError {
            print("Error: \(error)")
        }
    }
Run Code Online (Sandbox Code Playgroud)

输出:

Name: Bob,  age: 16, employed: No
Name: Vinny,  age: 56, employed: Yes
Run Code Online (Sandbox Code Playgroud)


Vas*_*huk 8

这里还有一个答案???

好的。坚持,稍等!之前的所有答案都是关于使用JSONSerialization,或返回 nil ,或忽略错误。

有什么不同

“我的解决方案”(这不是真正的我的解决方案,这是上述解决方案的混合)包含:

  1. 返回值的现代方式:(Result<Value,Error>返回值或错误)
  2. 避免nil使用
  3. 包含一个稍微详细的错误
  4. 使用扩展来拥有漂亮/直观的界面:Model.from(localJSON: "myJsonFile")
  5. 提供选择捆绑包的可能性

细节

  • Xcode 14
  • 斯威夫特 5.6.1

解决方案 1. JSON 文件 -> 可解码

enum JSONParseError: Error {
    case fileNotFound
    case dataInitialisation(error: Error)
    case decoding(error: Error)
}

extension Decodable {
    static func from(localJSON filename: String,
                     bundle: Bundle = .main) -> Result<Self, JSONParseError> {
        guard let url = bundle.url(forResource: filename, withExtension: "json") else {
            return .failure(.fileNotFound)
        }
        let data: Data
        do {
            data = try Data(contentsOf: url)
        } catch let error {
            return .failure(.dataInitialisation(error: error))
        }

        do {
            return .success(try JSONDecoder().decode(self, from: data))
        } catch let error {
            return .failure(.decoding(error: error))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案1 ​​使用方法

 struct Model: Decodable {
    let uuid: String
    let name: String
}

switch Model.from(localJSON: "myjsonfile") {
case .success(let value):
    print(value)
case .failure(let error):
    print(error)
}
Run Code Online (Sandbox Code Playgroud)

解决方案 2. JSON 文件 -> 字典

extension Dictionary where Key == String, Value == Any {

    enum JSONParseError: Error {
        case fileNotFound(filename: String)
        case dataInitialisation(Error)
        case jsonSerialization(Error)
        case mappingFail(value: Any, toType: Any)
    }

    static func from(JSONfile url: URL) -> Result<Self, JSONParseError> {
        let data: Data
        do {
            data = try Data(contentsOf: url)
        } catch let error {
            return .failure(.dataInitialisation(error))
        }

        let jsonObject: Any
        do {
            jsonObject = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
        } catch let error {
            return .failure(.jsonSerialization(error))
        }

        guard let jsonResult = jsonObject as? Self else {
            return .failure(.mappingFail(value: jsonObject, toType: Self.Type.self))
        }

        return .success(jsonResult)
    }

    static func from(localJSONfile name: String) -> Result<Self, JSONParseError> {
        let fileType = "json"
        let fullFileName = name + (name.contains(fileType) ? "" : ".\(fileType)")
        guard let path = Bundle.main.path(forResource: fullFileName, ofType: "") else {
            return .failure(.fileNotFound(filename: fullFileName))
        }
        return from(JSONfile: URL(fileURLWithPath: path))
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案2 使用方法

switch [String: Any].from(localJSONfile: "file.json") {
// OR switch [String: Any].from(localJSONfile: "file.json") {
// OR switch [String: Any].from(JSONfile: url) {
case let .success(dictionary):
    print(dictionary)
case let .failure(error):
    print("ERROR: \(error)")
}
Run Code Online (Sandbox Code Playgroud)


Abd*_*oor 7

这对我有用

func readjson(fileName: String) -> NSData{

    let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "json")
    let jsonData = NSData(contentsOfMappedFile: path!)

    return jsonData!
}
Run Code Online (Sandbox Code Playgroud)


wil*_*set 7

这是我使用SwiftyJSON的解决方案

if let path : String = NSBundle.mainBundle().pathForResource("filename", ofType: "json") {
    if let data = NSData(contentsOfFile: path) {

        let json = JSON(data: data)

    }
}
Run Code Online (Sandbox Code Playgroud)


小智 7

fileprivate class BundleTargetingClass {}
func loadJSON<T>(name: String) -> T? {
  guard let filePath = Bundle(for: BundleTargetingClass.self).url(forResource: name, withExtension: "json") else {
    return nil
  }

  guard let jsonData = try? Data(contentsOf: filePath, options: .mappedIfSafe) else {
    return nil
  }

  guard let json = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) else {
    return nil
  }

  return json as? T
}
Run Code Online (Sandbox Code Playgroud)

copy-paste ready,第三方框架独立解决方案.

用法

let json:[[String : AnyObject]] = loadJSON(name: "Stations")!


Pet*_*inz 7

Swift 4:试试我的解决方案:

测试文件

{
    "person":[
        {
            "name": "Bob",
            "age": "16",
            "employed": "No"
        },
        {
            "name": "Vinny",
            "age": "56",
            "employed": "Yes"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

RequestCodable.swift

import Foundation

struct RequestCodable:Codable {
    let person:[PersonCodable]
}
Run Code Online (Sandbox Code Playgroud)

PersonCodable.swift

import Foundation

struct PersonCodable:Codable {
    let name:String
    let age:String
    let employed:String
}
Run Code Online (Sandbox Code Playgroud)

可解码+FromJSON.swift

import Foundation

extension Decodable {

    static func fromJSON<T:Decodable>(_ fileName: String, fileExtension: String="json", bundle: Bundle = .main) throws -> T {
        guard let url = bundle.url(forResource: fileName, withExtension: fileExtension) else {
            throw NSError(domain: NSURLErrorDomain, code: NSURLErrorResourceUnavailable)
        }

        let data = try Data(contentsOf: url)

        return try JSONDecoder().decode(T.self, from: data)
    }
}
Run Code Online (Sandbox Code Playgroud)

例子:

let result = RequestCodable.fromJSON("test") as RequestCodable?

result?.person.compactMap({ print($0) }) 

/*
PersonCodable(name: "Bob", age: "16", employed: "No")
PersonCodable(name: "Vinny", age: "56", employed: "Yes")
*/
Run Code Online (Sandbox Code Playgroud)


Nev*_*oon 6

简化Peter Kreinz提供的示例。适用于Swift 4.2。

扩展功能:

extension Decodable {
  static func parse(jsonFile: String) -> Self? {
    guard let url = Bundle.main.url(forResource: jsonFile, withExtension: "json"),
          let data = try? Data(contentsOf: url),
          let output = try? JSONDecoder().decode(self, from: data)
        else {
      return nil
    }

    return output
  }
}
Run Code Online (Sandbox Code Playgroud)

示例模型:

struct Service: Decodable {
  let name: String
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

/// service.json
/// { "name": "Home & Garden" }

guard let output = Service.parse(jsonFile: "service") else {
// do something if parsing failed
 return
}

// use output if all good
Run Code Online (Sandbox Code Playgroud)

该示例也适用于数组:

/// services.json
/// [ { "name": "Home & Garden" } ]

guard let output = [Service].parse(jsonFile: "services") else {
// do something if parsing failed
 return
}

// use output if all good
Run Code Online (Sandbox Code Playgroud)

注意,我们如何不提供任何不必要的泛型,因此我们不需要转换解析结果。


Rob*_*Rob 5

我提供了另一个答案,因为这里的所有答案都不适合从测试包中加载资源.如果您正在使用一个发出JSON的远程服务,并且想要在不触及实际服务的情况下对结果进行单元测试,则需要一个或多个响应并将它们放入项目的Tests文件夹中的文件中.

func testCanReadTestJSONFile() {
    let path = NSBundle(forClass: ForecastIOAdapterTests.self).pathForResource("ForecastIOSample", ofType: "json")
    if let jsonData = NSData(contentsOfFile:path!) {
        let json = JSON(data: jsonData)
        if let currentTemperature = json["currently"]["temperature"].double {
            println("json: \(json)")
            XCTAssertGreaterThan(currentTemperature, 0)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这也使用SwiftyJSON,但获取测试包和加载文件的核心逻辑是问题的答案.


Abr*_*son 5

以下代码对我有用。我正在使用Swift 5

let path = Bundle.main.path(forResource: "yourJSONfileName", ofType: "json")
var jsonData = try! String(contentsOfFile: path!).data(using: .utf8)!
Run Code Online (Sandbox Code Playgroud)

然后,如果您的 Person Struct(或 Class)是可解码的(以及它的所有属性),您可以简单地执行以下操作:

let person = try! JSONDecoder().decode(Person.self, from: jsonData)
Run Code Online (Sandbox Code Playgroud)

我避免了所有错误处理代码以使代码更清晰。


小智 5

使用这个通用函数

func readJSONFromFile<T: Decodable>(fileName: String, type: T.Type) -> T? {
    if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
        do {
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode(T.self, from: data)
            return jsonData
        } catch {
            print("error:\(error)")
        }
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

用这行代码:

let model = readJSONFromFile(fileName: "Model", type: Model.self)
Run Code Online (Sandbox Code Playgroud)

对于这种类型:

struct Model: Codable {
    let tall: Int
}
Run Code Online (Sandbox Code Playgroud)