Parse and array of objects in JSON to an Object in Swift - json

Hey I have the following JSON:
{
"monuments": [
{
"name": "Iglesia de Tulyehualco",
"description": "No hay descripción",
"latitude": 19.2544877,
"longitude": -99.012157
},
{
"name": "Casa de Chuyin",
"description": "Casa de Jesús",
"latitude": 119.2563629,
"longitude": -99.0152632
}
]
}
I get the following code to try parse each object but I'm getting the error that type Any has no member 'x'.
func loadMonuments() {
if let path = Bundle.main.path(forResource: "monuments", 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 monumentsJson = jsonResult["monuments"] as? [Any] {
for m in monumentsJson {
print(m)
}
}
} catch {
// handle error
}
}
}
I want to get each property of the monument.

Option1:(recommended)
struct Root:Decodable {
let monuments:[InnerItem]
}
struct InnerItem:Decodable {
let name:String
let description:String
let latitude:Doube
let longitude:Double
}
//
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options:[])
let content = try JSONDecoder().decode(Root.self,from:data)
print(content)
}
catch {
print(error)
}
Option2:
if let jsonResult = jsonResult as? [String:Any] , let monumentsJson = jsonResult["monuments"] as? [[String:Any]] {
for m in monumentsJson {
print(m["name"])
}
}

In Swift4 was introduced Codable for serialization, so you must try to make your objects Codable like this:
struct Monument: Codable {
let name: String
let description: String
let latitude: String
let longitude: String
}
And then you can parse the object using this:
func loadMonuments() -> [Monument] {
guard let path = Bundle.main.path(forResource: "monuments", ofType: "json"),
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) else {
return []
}
do {
return try JSONDecoder().decode([Monument].self, from: data)
} catch {
print("error: \(error)")
return []
}
}

Related

Cannot parse JSON in Swift. "Expected to decode Dictionary<String, Any> but found an array instead." [duplicate]

This question already has answers here:
Decoding Error -- Expected to decode Dictionary<String, Any> but found an array instead
(2 answers)
Closed 1 year ago.
I try to retrieve data from JSON.
Below my code with structs:
private func ParseJSON() {
guard let path = Bundle.main.path(forResource: "dataDictionary", ofType: "json")
else {return}
let url = URL(fileURLWithPath: path)
var result: Result?
do {
let jsonData = try Data(contentsOf: url)
result = try JSONDecoder().decode(Result.self, from: jsonData)
if let result = result {
print(result)
} else {
print("Failed to parse")
}
return
}
catch {
print("Error:\(error)")
}
}
struct Phonetic: Codable {
let text: String?
let audio: String?
}
struct Result: Codable {
let phonetic : String?
let phonetics : [Phonetic]?
let word : String?
}
And here's my JSON-sample specified in dataDictionary.json:
[{
"word": "castle",
"phonetic": "ˈkɑːs(ə)l",
"phonetics": [{
"text": "ˈkɑːs(ə)l",
"audio": "//ssl.gstatic.com/dictionary/static/sounds/20200429/castle--1_gb_1.mp3"
}]
}]
Please help me to write my structs correctly.
Your JSON is an array so it should be treated as such.
private func ParseJSON() {
guard let path = Bundle.main.path(
forResource: "dataDictionary", ofType: "json") else { fatalError() }
let url = URL(fileURLWithPath: path)
do {
let jsonData = try Data(contentsOf: url)
let result = try JSONDecoder().decode([Result].self, from: jsonData)
print("Result: \(result)")
} catch {
print("Error:\(error)")
}
}
Just a nitpick, but if you can be sure that your JSON fields are non-null then you can change your model properties to be non-optional:
struct Phonetic: Codable {
let text: String
let audio: String
}
struct Result: Codable {
let phonetic: String
let phonetics: [Phonetic]
let word: String
}

Swift weather data from Openweathermap API?

From openweathermap api, I am getting below response.
{
"cod":"200",
"message":0,
"cnt":40,
"list":[
{
"dt":1587643200,
"main":{
"temp":289.78,
"feels_like":283.61,
"temp_min":289.03,
"temp_max":289.78,
"pressure":1014,
"sea_level":1014,
"grnd_level":1010,
"humidity":41,
"temp_kf":0.75
},
"weather":[
{
"id":804,
"main":"Clouds",
"description":"overcast clouds",
"icon":"04d"
}
],
"clouds":{
"all":94
},
"wind":{
"speed":6.75,
"deg":2
},
"sys":{
"pod":"d"
},
"dt_txt":"2020-04-23 12:00:00"
},
{
"dt":1587654000,
"main":{
"temp":289.66,
"feels_like":284.44,
"temp_min":289.34,
"temp_max":289.66,
"pressure":1013,
"sea_level":1013,
"grnd_level":1009,
"humidity":47,
"temp_kf":0.32
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04d"
}
],
"clouds":{
"all":67
},
"wind":{
"speed":5.9,
"deg":357
},
"sys":{
"pod":"d"
},
"dt_txt":"2020-04-23 15:00:00"
}
Then I write the code below to get wind data for any specific daytime(dt). I get the jsonresponse to the Any "list". But I can't get the wind data. I get the error
"Value of type 'Any' has no subscripts".
Also, I can't understand how can I get the wind data for dt=1587643200 and dt=1587654000 separately.
if let list = jsonresponse["list"] as? Any {
let wind = list["wind"] as? [String : Any],
print(wind)
}
This is a super simple example, this question is similar to your problem. I would like that you learn about Codable protocol to simplify and improve your code because of this way is super creepy.
let url = URL(string: "https://samples.openweathermap.org/data/2.5/history/city?id=2885679&type=hour&appid=b1b15e88fa797225412429c1c50c122a1")!
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Error with the response, unexpected status code: \(String(describing: response))")
return
}
guard let data = data else {
return
}
guard let dictionaryObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return
}
guard let list = dictionaryObj["list"] as? [[String: Any]] else {
return
}
if let first = list.first, let wind = first["wind"] {
print(wind)
}
}).resume()
Incase anyone further needs help, I wrote a small code segment—using Swift Codables—to get this done.
Note that I'm using Higher Order Functions in Swift to filter & print the required values. You can follow this tutorial if you need to know more about how they work..
let urlNew = "https://samples.openweathermap.org/data/2.5/history/city?id=2885679&type=hour&appid=b1b15e88fa797225412429c1c50c122a1"
struct WeatherResponse: Codable {
let message: String
let cod: String
let city_id: Int
let calctime: Double
let cnt: Int
let list: [WeatherCompositeObject]
struct WeatherCompositeObject: Codable {
let main: WeatherMainObject
let wind: WeatherWindObject
let clouds: WeatherCloudObject
let weather: [WeatherObject]
let dt: Int
struct WeatherMainObject: Codable {
let temp: Double
let humidity: Int
//You can add the other parameters as needed here
}
struct WeatherWindObject: Codable {
let speed: Double
let deg: Double
}
struct WeatherCloudObject: Codable {
let all: Int
}
struct WeatherObject: Codable {
let id: Int
let main: String
let description: String
}
}
}
class ApiContentDownloader {
func getCurrentWeather(url: String) {
URLSession.shared.dataTask(with: URL(string: url)!) { data, urlResponse, error in
let parser = ContentParser()
let result = parser.parseData(data: data!)
if let result = result {
print("Weather: \(result.message)")
print("DT values: \(result.list.map({ $0.dt})) ")
print("Wind values: \(result.list.map({ $0.wind.speed})) ")
print("Weather descriptions: \(result.list.map({ $0.weather.map( {$0.description} )})) ")
}
else {
print("Oops... Error occured")
}
}.resume()
}
}
class ContentParser {
func parseData(data: Data) -> WeatherResponse? {
var result: WeatherResponse? = nil
do {
let json = try JSONDecoder().decode(WeatherResponse.self, from: data)
result = json
}
catch {
print(error.localizedDescription)
}
return result
}
}
let downloader = ApiContentDownloader()
downloader.getCurrentWeather(url: urlNew)

How to convert Raw dictionary to JSON string?

I have this type of dictionary:
{
“event”: {
“type”: “message_create”, “message_create”: {
“target”: {
“recipient_id”: “RECIPIENT_USER_ID”
}, “message_data”: {
“text”: “Hello World!”,
}
}
}
I want to convert this dictionary into JSON string format, but not able to do it. Please anyone guide me on this.
This is the code till I am trying after sending message I am getting bad authenticating data message from server -
func sendMessage(_ userInfo: String) {
let requestURL = URL(string: "https://api.twitter.com/1.1/direct_messages/events/new.json")
Alamofire.request(requestURL!, method: .post,
encoding: URLEncoding.default,
headers: getRequestHeader()).responseJSON { (response) in
print(response)
switch response.result {
case .success:
print("Validation Successful")
case .failure(let error):
print("FAILURE ERROR : \(error.localizedDescription)")
print("ERROR : \(error)")
if let data = response.data {
print("Print Server Error: " + String(data: data, encoding: String.Encoding.utf8)!)
}
}
}
}
// MARK: - Get Request Header
func getRequestHeader() -> ([String: String]) {
return ["authorization": "","CONSUMER_KEY":"",
"CONSUMER_SECRET":"",
"oauth_secret":"7W2Gx4KEjz7d164NPvJaOktzhaSPpV3VNjvyjpIqaDc02",
"oauth_token":"2605063830-IffuOmn2tEajFXY6khbzmeMwNoUvGkQ8qrYonzw",
"oauth_version":"1.0",
"oauth_signature_method":"HMAC-SHA1",
"Content-Type": "application/json"
]
}
For the purpose of focusing on getting the json string (without caring about how you got the dictionary), I would assume that your myDict is the dictionary that you want to convert to json string:
let json = """
{
"event": {
"type": "message_create", "message_create": {
"target": {
"recipient_id": "RECIPIENT_USER_ID"
},
"message_data": {
"text": "Hello World!"
}
}
}
}
""".data(using: .utf8)
var myDict: [String: Any] = [: ]
do {
if let dict = try JSONSerialization.jsonObject(with: json!, options: []) as? [String: Any] {
myDict = dict
}
} catch {
print(error)
}
So now we want to convert myDict as a json string:
do {
// converting `myDict` to Json Data, and then
// getting our json as string from the `jsonData`:
if let jsonData = try JSONSerialization.data(withJSONObject: myDict, options: []) as? Data,
let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
} catch {
print(error)
}
jsonString is now what are you looking for! you should see on the log:
{"event":{"type":"message_create","message_create":{"target":{"recipient_id":"RECIPIENT_USER_ID"},"message_data":{"text":"Hello
World!"}}}}
which is perfectly valid json.
public extension NSDictionary{
public func toJSONString() -> String{
if JSONSerialization.isValidJSONObject(self) {
do{
let data = try JSONSerialization.data(withJSONObject: self, options: JSONSerialization.WritingOptions(rawValue: 0))
if let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
return string as String
}
}catch {
print("error")
}
}
return ""
}
}

Json parse array in dictionary

I use json api in my application. It can check a company does use electronic Invoice. I have a json data like that:
{
"ErrorStatus": null,
"Result": {
"CustomerList": [
{
"RegisterNumber": "6320036072",
"Title": "VATAN BİLGİSAYAR SANAYİ VE TİCARET ANONİM ŞİRKETİ",
"Alias": "urn:mail:defaultpk#vatanbilgisayar.com",
"Type": "Özel",
"FirstCreationTime": "2014-01-01T05:35:20",
"AliasCreationTime": "2014-01-01T05:35:20"
}
],
"ISEInvoiceCustomer": true
} }
and i use that fucntion for get json data:
func getClientQuery(authorization:String) {
let url = NSURL(string: URLCustomerCheck+strRegisterNumber)
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue(authorization, forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
if error != nil {
let alert = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
} else {
if data != nil {
do {
let jSONResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String,AnyObject>
DispatchQueue.main.async {
print(jSONResult)
let result = jSONResult["Result"] as! [String:AnyObject]
//let customerList = result["CustomerList"] as! [[String:AnyObject]]
let ISEInvoiceCustomer = String(describing: result["ISEInvoiceCustomer"])
self._lblISEinvoiceCustomer.text = " \(ISEInvoiceCustomer) "
}
} catch {
}
}
}
}
task.resume()
}
My question is how can i parse "RegisterNumber", "Title".. in "CustomerList"? It's a array that have a member. However i can not parse it in my function.
The customerList line you commented out is needed. Then iterate that array and pull out whatever values you want from each dictionary.
And you really should avoid us as! or any other forced unwrapping when working with JSON. You don't want your app to crash when you obtain unexpected data.
And never use String(describing:) to create a value you will display to a user. The result is inappropriate for display. It's only to be used for debugging purposes.
if let jSONResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String:Any]
DispatchQueue.main.async {
print(jSONResult)
if let result = jSONResult["Result"] as? [String:AnyObject],
let customerList = result["CustomerList"] as? [[String:Any]] {
for customer in customList {
let registrationNumber = customer["RegisterNumber"]
// and any others you need
}
let ISEInvoiceCustomer = result["ISEInvoiceCustomer"] as? Bool ?? false
self._lblISEinvoiceCustomer.text = ISEInvoiceCustomer) ? "Yes" : "No"
}
}
}
Better to Map json to Model , this become easy using Codable
import Foundation
struct Client: Codable {
let errorStatus: ErrorStatus?
let result: Result
enum CodingKeys: String, CodingKey {
case errorStatus = "ErrorStatus"
case result = "Result"
}
}
struct ErrorStatus: Codable {
}
struct Result: Codable {
let customerList: [CustomerList]
let iseInvoiceCustomer: Bool
enum CodingKeys: String, CodingKey {
case customerList = "CustomerList"
case iseInvoiceCustomer = "ISEInvoiceCustomer"
}
}
struct CustomerList: Codable {
let registerNumber, title, alias, type: String
let firstCreationTime, aliasCreationTime: String
enum CodingKeys: String, CodingKey {
case registerNumber = "RegisterNumber"
case title = "Title"
case alias = "Alias"
case type = "Type"
case firstCreationTime = "FirstCreationTime"
case aliasCreationTime = "AliasCreationTime"
}
}
// MARK: Convenience initializers
extension Client {
init(data: Data) throws {
self = try JSONDecoder().decode(Client.self, from: data)
}
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
}
Get customerList :
func getClientQuery(authorization:String) {
let url = NSURL(string: URLCustomerCheck+strRegisterNumber)
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue(authorization, forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
if error != nil {
let alert = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
} else {
if data != nil {
if let client = try? Client.init(data: data){
client.result.customerList.forEach { (customer) in
print(customer.registerNumber)
}
}
}
}
}
task.resume()
}
let data = resultData
do {
guard let JSONResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : AnyObject],
let resultObject = JSONResult["Result"] as? [String : AnyObject],
let customerList = resultObject["CustomerList"] as? [Anyobject]
else { return }
// Loop the array of objects
for object in customerList {
let registerNumber = object["RegisterNumber"] as? String
let title = object["Title"] as? String
let alias = object["Alias"] as? String
let type = object["Type"] as? String
let firstCreationTime = object["FirstCreationTime"] as? String // Or as a DateObject
let aliasCreationTime = object["AliasCreationTime"] as? String // Or as a DateObject
}
let isEInvoiceCustomer = resultObject["ISEInvoiceCustomer"] as? Bool
} catch {
print(error)
}

Reading in a JSON File Using Swift

I'm really struggling with trying to read a JSON file into Swift so I can play around with it. I've spent the best part of 2 days re-searching and trying different methods but no luck as of yet so I have signed up to StackOverFlow to see if anyone can point me in the right direction.....
My JSON file is called test.json and contains the following:
{
"person":[
{
"name": "Bob",
"age": "16",
"employed": "No"
},
{
"name": "Vinny",
"age": "56",
"employed": "Yes"
}
]
}
The file is stored in the documents directly and I access it using the following code:
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.....
If anyone can just give me a push in the right direction on how I can de-serialize the JSON file and put it in an accessible Swift object I will be eternally grateful!
Kind Regards,
Krivvenz.
Follow the below code :
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
}
}
}
}
The array "persons" will contain all data for key person. Iterate throughs to fetch it.
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
}
}
Swift 4.x and 5.x using 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
}
Swift 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
}
If anyone is looking for SwiftyJSON Answer:
Update:
For 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.")
}
Swift 5.1, Xcode 11
You can use this:
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
}
Xcode 8 Swift 3 read json from file update:
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 {}
}
Simplifying the example provided by Peter Kreinz. Works with Swift 4.2.
The extension function:
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
}
}
The example model:
struct Service: Decodable {
let name: String
}
The example usage:
/// service.json
/// { "name": "Home & Garden" }
guard let output = Service.parse(jsonFile: "service") else {
// do something if parsing failed
return
}
// use output if all good
The example will work with arrays, too:
/// services.json
/// [ { "name": "Home & Garden" } ]
guard let output = [Service].parse(jsonFile: "services") else {
// do something if parsing failed
return
}
// use output if all good
Notice how we don't provide any unnecessary generics, thus we don't need to cast the result of parse.
Updated names for Swift 3.0
Based on Abhishek's answer and Druva's answer
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
}
Swift 2.1 answer (based on Abhishek's) :
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 {}
}
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)")
}
}
Output:
Name: Bob, age: 16, employed: No
Name: Vinny, age: 56, employed: Yes
This worked great with me
func readjson(fileName: String) -> NSData{
let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "json")
let jsonData = NSData(contentsOfMappedFile: path!)
return jsonData!
}
Here is my solution using SwiftyJSON
if let path : String = NSBundle.mainBundle().pathForResource("filename", ofType: "json") {
if let data = NSData(contentsOfFile: path) {
let json = JSON(data: data)
}
}
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
}
👆🏻 copy-paste ready, 3rd party framework independent solution.
usage 👇🏻
let json:[[String : AnyObject]] = loadJSON(name: "Stations")!
Swift 4: Try out my solution:
test.json
{
"person":[
{
"name": "Bob",
"age": "16",
"employed": "No"
},
{
"name": "Vinny",
"age": "56",
"employed": "Yes"
}
]
}
RequestCodable.swift
import Foundation
struct RequestCodable:Codable {
let person:[PersonCodable]
}
PersonCodable.swift
import Foundation
struct PersonCodable:Codable {
let name:String
let age:String
let employed:String
}
Decodable+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)
}
}
Example:
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")
*/
Use this generic function
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
}
with this line of code:
let model = readJSONFromFile(fileName: "Model", type: Model.self)
for this type:
struct Model: Codable {
let tall: Int
}
I'm providing another answer because none of the ones here are geared toward loading the resource from the test bundle. If you are consuming a remote service that puts out JSON and want to unit test parsing the results without hitting the actual service, you take one or more responses and put them into files in the Tests folder in your project.
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)
}
}
}
This also uses SwiftyJSON but the core logic of getting the test bundle and loading the file is the answer to the question.
The following code works for me. I am using Swift 5
let path = Bundle.main.path(forResource: "yourJSONfileName", ofType: "json")
var jsonData = try! String(contentsOfFile: path!).data(using: .utf8)!
Then, if your Person Struct (or Class) is Decodable (and also all of its properties), you can simply do:
let person = try! JSONDecoder().decode(Person.self, from: jsonData)
I avoided all the error handling code to make the code more legible.
Updated for Swift 3 with safest way
private func readLocalJsonFile() {
if let urlPath = Bundle.main.url(forResource: "test", withExtension: "json") {
do {
let jsonData = try Data(contentsOf: urlPath, options: .mappedIfSafe)
if let jsonDict = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String: AnyObject] {
if let personArray = jsonDict["person"] as? [[String: AnyObject]] {
for personDict in personArray {
for (key, value) in personDict {
print(key, value)
}
print("\n")
}
}
}
}
catch let jsonError {
print(jsonError)
}
}
}
Latest swift 3.0 absolutely working
func loadJson(filename fileName: String) -> [String: AnyObject]?
{
if let url = Bundle.main.url(forResource: fileName, withExtension: "json")
{
if let data = NSData(contentsOf: url) {
do {
let object = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
return dictionary
}
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
print("Error!! Unable to load \(fileName).json")
}
return nil
}
Swift 4 JSON to Class with Decodable - for those who prefer classes
Define the classes as follows:
class People: Decodable {
var person: [Person]?
init(fileName : String){
// url, data and jsonData should not be nil
guard let url = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let data = try? Data(contentsOf: url) else { return }
guard let jsonData = try? JSONDecoder().decode(People.self, from: data) else { return }
// assigns the value to [person]
person = jsonData.person
}
}
class Person : Decodable {
var name: String
var age: String
var employed: String
}
Usage, pretty abstract:
let people = People(fileName: "people")
let personArray = people.person
This allow methods for both People and Person classes, variables (attributes) and methods can also marked as private if needed.
Swift 5+
Decoding the jsonData with your Struct
if let jsonData = readFile(forName: <your file name>) {
do {
let decodedData = try JSONDecoder().decode(<your struct name>.self, from: jsonData)
return decodedData.<what you expect>
} catch { print("JSON decode error") }
}
This will read the file and return jsonData
In case you are actually in another bundle (test for instance), use:
let bundlePath = Bundle(for: type(of: self)).path(forResource: name, ofType: "json")
private func readFile(forName name: String) -> Data? {
do {
if let bundlePath = Bundle.main.path(forResource: name, ofType: "json"),
let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) {
return jsonData
}
} catch {
print(error)
}
return nil
}
I wasted my time in locating file which was located in my project with name Jsondata.json. But I weren't able to locate my File through code....
Solution: Make sure that your Jsondata.json file is added in Project> Build Phases> Copy Bundle Resources. Otherwise you wont be able to get file and Bundle.main.url(forResource: fileName, withExtension: "json") will give you nil always.
One more answer here???
Ok. Hold on! All of the answers before were about using JSONSerialization, or returns nil, or ignores errors.
What is the different
"My solution" (is is not really my, this is a mix of the solutions above) contains:
Modern way to return values: Result<Value,Error> (returns Value or Error)
Avoids nil usage
Contains a slightly verbose error
Uses extension to have pretty/intuitive interface: Model.from(localJSON: "myJsonFile")
Gives possibility to select bundle
Details
Xcode 14
Swift 5.6.1
Solution 1. JSON file -> Decodable
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))
}
}
}
Solution 1 Usage
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)
}
Solution 2. JSON file -> Dictionary
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))
}
}
Solution 2 Usage
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)")
}
Based on Abhishek's answer, for iOS 8 this would be:
let masterDataUrl: NSURL = NSBundle.mainBundle().URLForResource("masterdata", withExtension: "json")!
let jsonData: NSData = NSData(contentsOfURL: masterDataUrl)!
let jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: nil) as! NSDictionary
var persons : NSArray = jsonResult["person"] as! NSArray
This worked for me with XCode 8.3.3
func fetchPersons(){
if let pathURL = Bundle.main.url(forResource: "Person", withExtension: "json"){
do {
let jsonData = try Data(contentsOf: pathURL, options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String: Any]
if let persons = jsonResult["person"] as? [Any]{
print(persons)
}
}catch(let error){
print (error.localizedDescription)
}
}
}
Swift 4.1 Updated Xcode 9.2
if let filePath = Bundle.main.path(forResource: "fileName", ofType: "json"), let data = NSData(contentsOfFile: filePath) {
do {
let json = try JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.allowFragments)
}
catch {
//Handle error
}
}
//change type based on your struct and right JSON file
let quoteData: [DataType] =
load("file.json")
func load<T: Decodable>(_ filename: String, as type: T.Type = T.self) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
Swift 5 answer worked for me, except that is missing that i must add a empty file, rename it as xxx.json after it works, and using generics.
func loadJson<T:Codable>(filename fileName: String) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
print("error:\(error)")
}
}
return nil
}
code
I’ve used below code to fetch JSON from FAQ-data.json file present in project directory .
I’m implementing in Xcode 7.3 using Swift.
func fetchJSONContent() {
if let path = NSBundle.mainBundle().pathForResource("FAQ-data", ofType: "json") {
if let jsonData = NSData(contentsOfFile: path) {
do {
if let jsonResult: NSDictionary = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary {
if let responseParameter : NSDictionary = jsonResult["responseParameter"] as? NSDictionary {
if let response : NSArray = responseParameter["FAQ"] as? NSArray {
responseFAQ = response
print("response FAQ : \(response)")
}
}
}
}
catch { print("Error while parsing: \(error)") }
}
}
}
override func viewWillAppear(animated: Bool) {
fetchFAQContent()
}
Structure of JSON file :
{
"status": "00",
"msg": "FAQ List ",
"responseParameter": {
"FAQ": [
{
"question": “Question No.1 here”,
"answer": “Answer goes here”,
"id": 1
},
{
"question": “Question No.2 here”,
"answer": “Answer goes here”,
"id": 2
}
. . .
]
}
}
I might also recommend Ray Wenderlich's Swift JSON Tutorial (which also covers the awesome SwiftyJSON alternative, Gloss). An excerpt (which granted, by itself, does not fully answer the poster, but the added value of this answer is the link, so no -1's for that, please):
In Objective-C, parsing and deserializing JSON is fairly straightforward:
NSArray *json = [NSJSONSerialization JSONObjectWithData:JSONData
options:kNilOptions error:nil];
NSString *age = json[0][#"person"][#"age"];
NSLog(#"Dani's age is %#", age);
In Swift, parsing and deserializing JSON is a little more tedious due to Swift optionals and type-safety [but as] part of Swift 2.0 the guard statement was introduced to help get rid of nested if statements:
var json: Array!
do {
json = try NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions()) as? Array
} catch {
print(error)
}
guard let item = json[0] as? [String: AnyObject],
let person = item["person"] as? [String: AnyObject],
let age = person["age"] as? Int else {
return;
}
print("Dani's age is \(age)")
Of course, in XCode 8.x, you just double-tap the space bar and say "Hey, Siri, please deserialize this JSON for me in Swift 3.0 with space/tab-indents."
SWIFTYJSON VERSION SWIFT 3
func loadJson(fileName: String) -> JSON {
var dataPath:JSON!
if let path : String = Bundle.main.path(forResource: fileName, ofType: "json") {
if let data = NSData(contentsOfFile: path) {
dataPath = JSON(data: data as Data)
}
}
return dataPath
}