Accessing nested JSON data [duplicate] - json

I want to make one function in my swift project that converts String to Dictionary json format but I got one error:
Cannot convert expression's type (#lvalue NSData,options:IntegerLitralConvertible ...
This is my code:
func convertStringToDictionary (text:String) -> Dictionary<String,String> {
var data :NSData = text.dataUsingEncoding(NSUTF8StringEncoding)!
var json :Dictionary = NSJSONSerialization.JSONObjectWithData(data, options:0, error: nil)
return json
}
I make this function in Objective-C :
- (NSDictionary*)convertStringToDictionary:(NSString*)string {
NSError* error;
//giving error as it takes dic, array,etc only. not custom object.
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
return json;
}

Warning: this is a convenience method to convert a JSON string to a dictionary if, for some reason, you have to work from a JSON string. But if you have the JSON data available, you should instead work with the data, without using a string at all.
Swift 3
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
let str = "{\"name\":\"James\"}"
let dict = convertToDictionary(text: str)
Swift 2
func convertStringToDictionary(text: String) -> [String:AnyObject]? {
if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
do {
return try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String:AnyObject]
} catch let error as NSError {
print(error)
}
}
return nil
}
let str = "{\"name\":\"James\"}"
let result = convertStringToDictionary(str)
Original Swift 1 answer:
func convertStringToDictionary(text: String) -> [String:String]? {
if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
var error: NSError?
let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:String]
if error != nil {
println(error)
}
return json
}
return nil
}
let str = "{\"name\":\"James\"}"
let result = convertStringToDictionary(str) // ["name": "James"]
if let name = result?["name"] { // The `?` is here because our `convertStringToDictionary` function returns an Optional
println(name) // "James"
}
In your version, you didn't pass the proper parameters to NSJSONSerialization and forgot to cast the result. Also, it's better to check for the possible error. Last note: this works only if your value is a String. If it could be another type, it would be better to declare the dictionary conversion like this:
let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:AnyObject]
and of course you would also need to change the return type of the function:
func convertStringToDictionary(text: String) -> [String:AnyObject]? { ... }

I've updated Eric D's answer for Swift 5:
func convertStringToDictionary(text: String) -> [String:AnyObject]? {
if let data = text.data(using: .utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject]
return json
} catch {
print("Something went wrong")
}
}
return nil
}

Swift 3:
if let data = text.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any]
print(json)
} catch {
print("Something went wrong")
}
}

With Swift 3, JSONSerialization has a method called json​Object(with:​options:​). json​Object(with:​options:​) has the following declaration:
class func jsonObject(with data: Data, options opt: JSONSerialization.ReadingOptions = []) throws -> Any
Returns a Foundation object from given JSON data.
When you use json​Object(with:​options:​), you have to deal with error handling (try, try? or try!) and type casting (from Any). Therefore, you can solve your problem with one of the following patterns.
#1. Using a method that throws and returns a non-optional type
import Foundation
func convertToDictionary(from text: String) throws -> [String: String] {
guard let data = text.data(using: .utf8) else { return [:] }
let anyResult: Any = try JSONSerialization.jsonObject(with: data, options: [])
return anyResult as? [String: String] ?? [:]
}
Usage:
let string1 = "{\"City\":\"Paris\"}"
do {
let dictionary = try convertToDictionary(from: string1)
print(dictionary) // prints: ["City": "Paris"]
} catch {
print(error)
}
let string2 = "{\"Quantity\":100}"
do {
let dictionary = try convertToDictionary(from: string2)
print(dictionary) // prints [:]
} catch {
print(error)
}
let string3 = "{\"Object\"}"
do {
let dictionary = try convertToDictionary(from: string3)
print(dictionary)
} catch {
print(error) // prints: Error Domain=NSCocoaErrorDomain Code=3840 "No value for key in object around character 9." UserInfo={NSDebugDescription=No value for key in object around character 9.}
}
#2. Using a method that throws and returns an optional type
import Foundation
func convertToDictionary(from text: String) throws -> [String: String]? {
guard let data = text.data(using: .utf8) else { return [:] }
let anyResult: Any = try JSONSerialization.jsonObject(with: data, options: [])
return anyResult as? [String: String]
}
Usage:
let string1 = "{\"City\":\"Paris\"}"
do {
let dictionary = try convertToDictionary(from: string1)
print(String(describing: dictionary)) // prints: Optional(["City": "Paris"])
} catch {
print(error)
}
let string2 = "{\"Quantity\":100}"
do {
let dictionary = try convertToDictionary(from: string2)
print(String(describing: dictionary)) // prints nil
} catch {
print(error)
}
let string3 = "{\"Object\"}"
do {
let dictionary = try convertToDictionary(from: string3)
print(String(describing: dictionary))
} catch {
print(error) // prints: Error Domain=NSCocoaErrorDomain Code=3840 "No value for key in object around character 9." UserInfo={NSDebugDescription=No value for key in object around character 9.}
}
#3. Using a method that does not throw and returns a non-optional type
import Foundation
func convertToDictionary(from text: String) -> [String: String] {
guard let data = text.data(using: .utf8) else { return [:] }
let anyResult: Any? = try? JSONSerialization.jsonObject(with: data, options: [])
return anyResult as? [String: String] ?? [:]
}
Usage:
let string1 = "{\"City\":\"Paris\"}"
let dictionary1 = convertToDictionary(from: string1)
print(dictionary1) // prints: ["City": "Paris"]
let string2 = "{\"Quantity\":100}"
let dictionary2 = convertToDictionary(from: string2)
print(dictionary2) // prints: [:]
let string3 = "{\"Object\"}"
let dictionary3 = convertToDictionary(from: string3)
print(dictionary3) // prints: [:]
#4. Using a method that does not throw and returns an optional type
import Foundation
func convertToDictionary(from text: String) -> [String: String]? {
guard let data = text.data(using: .utf8) else { return nil }
let anyResult = try? JSONSerialization.jsonObject(with: data, options: [])
return anyResult as? [String: String]
}
Usage:
let string1 = "{\"City\":\"Paris\"}"
let dictionary1 = convertToDictionary(from: string1)
print(String(describing: dictionary1)) // prints: Optional(["City": "Paris"])
let string2 = "{\"Quantity\":100}"
let dictionary2 = convertToDictionary(from: string2)
print(String(describing: dictionary2)) // prints: nil
let string3 = "{\"Object\"}"
let dictionary3 = convertToDictionary(from: string3)
print(String(describing: dictionary3)) // prints: nil

Swift 5
extension String {
func convertToDictionary() -> [String: Any]? {
if let data = data(using: .utf8) {
return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
}
return nil
}
}

Swift 4
extension String {
func convertToDictionary() -> [String: Any]? {
if let data = self.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
}

Details
Xcode Version 10.3 (10G8), Swift 5
Solution
import Foundation
// MARK: - CastingError
struct CastingError: Error {
let fromType: Any.Type
let toType: Any.Type
init<FromType, ToType>(fromType: FromType.Type, toType: ToType.Type) {
self.fromType = fromType
self.toType = toType
}
}
extension CastingError: LocalizedError {
var localizedDescription: String { return "Can not cast from \(fromType) to \(toType)" }
}
extension CastingError: CustomStringConvertible { var description: String { return localizedDescription } }
// MARK: - Data cast extensions
extension Data {
func toDictionary(options: JSONSerialization.ReadingOptions = []) throws -> [String: Any] {
return try to(type: [String: Any].self, options: options)
}
func to<T>(type: T.Type, options: JSONSerialization.ReadingOptions = []) throws -> T {
guard let result = try JSONSerialization.jsonObject(with: self, options: options) as? T else {
throw CastingError(fromType: type, toType: T.self)
}
return result
}
}
// MARK: - String cast extensions
extension String {
func asJSON<T>(to type: T.Type, using encoding: String.Encoding = .utf8) throws -> T {
guard let data = data(using: encoding) else { throw CastingError(fromType: type, toType: T.self) }
return try data.to(type: T.self)
}
func asJSONToDictionary(using encoding: String.Encoding = .utf8) throws -> [String: Any] {
return try asJSON(to: [String: Any].self, using: encoding)
}
}
// MARK: - Dictionary cast extensions
extension Dictionary {
func toData(options: JSONSerialization.WritingOptions = []) throws -> Data {
return try JSONSerialization.data(withJSONObject: self, options: options)
}
}
Usage
let value1 = try? data.toDictionary()
let value2 = try? data.to(type: [String: Any].self)
let value3 = try? data.to(type: [String: String].self)
let value4 = try? string.asJSONToDictionary()
let value5 = try? string.asJSON(to: [String: String].self)
Test sample
Do not forget to paste the solution code here
func testDescriber(text: String, value: Any) {
print("\n//////////////////////////////////////////")
print("-- \(text)\n\n type: \(type(of: value))\n value: \(value)")
}
let json1: [String: Any] = ["key1" : 1, "key2": true, "key3" : ["a": 1, "b": 2], "key4": [1,2,3]]
var jsonData = try? json1.toData()
testDescriber(text: "Sample test of func toDictionary()", value: json1)
if let data = jsonData {
print(" Result: \(String(describing: try? data.toDictionary()))")
}
testDescriber(text: "Sample test of func to<T>() -> [String: Any]", value: json1)
if let data = jsonData {
print(" Result: \(String(describing: try? data.to(type: [String: Any].self)))")
}
testDescriber(text: "Sample test of func to<T>() -> [String] with cast error", value: json1)
if let data = jsonData {
do {
print(" Result: \(String(describing: try data.to(type: [String].self)))")
} catch {
print(" ERROR: \(error)")
}
}
let array = [1,4,5,6]
testDescriber(text: "Sample test of func to<T>() -> [Int]", value: array)
if let data = try? JSONSerialization.data(withJSONObject: array) {
print(" Result: \(String(describing: try? data.to(type: [Int].self)))")
}
let json2 = ["key1": "a", "key2": "b"]
testDescriber(text: "Sample test of func to<T>() -> [String: String]", value: json2)
if let data = try? JSONSerialization.data(withJSONObject: json2) {
print(" Result: \(String(describing: try? data.to(type: [String: String].self)))")
}
let jsonString = "{\"key1\": \"a\", \"key2\": \"b\"}"
testDescriber(text: "Sample test of func to<T>() -> [String: String]", value: jsonString)
print(" Result: \(String(describing: try? jsonString.asJSON(to: [String: String].self)))")
testDescriber(text: "Sample test of func to<T>() -> [String: String]", value: jsonString)
print(" Result: \(String(describing: try? jsonString.asJSONToDictionary()))")
let wrongJsonString = "{\"key1\": \"a\", \"key2\":}"
testDescriber(text: "Sample test of func to<T>() -> [String: String] with JSONSerialization error", value: jsonString)
do {
let json = try wrongJsonString.asJSON(to: [String: String].self)
print(" Result: \(String(describing: json))")
} catch {
print(" ERROR: \(error)")
}
Test log
//////////////////////////////////////////
-- Sample test of func toDictionary()
type: Dictionary<String, Any>
value: ["key4": [1, 2, 3], "key2": true, "key3": ["a": 1, "b": 2], "key1": 1]
Result: Optional(["key4": <__NSArrayI 0x600002a35380>(
1,
2,
3
)
, "key2": 1, "key3": {
a = 1;
b = 2;
}, "key1": 1])
//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: Any]
type: Dictionary<String, Any>
value: ["key4": [1, 2, 3], "key2": true, "key3": ["a": 1, "b": 2], "key1": 1]
Result: Optional(["key4": <__NSArrayI 0x600002a254d0>(
1,
2,
3
)
, "key2": 1, "key1": 1, "key3": {
a = 1;
b = 2;
}])
//////////////////////////////////////////
-- Sample test of func to<T>() -> [String] with cast error
type: Dictionary<String, Any>
value: ["key4": [1, 2, 3], "key2": true, "key3": ["a": 1, "b": 2], "key1": 1]
ERROR: Can not cast from Array<String> to Array<String>
//////////////////////////////////////////
-- Sample test of func to<T>() -> [Int]
type: Array<Int>
value: [1, 4, 5, 6]
Result: Optional([1, 4, 5, 6])
//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String]
type: Dictionary<String, String>
value: ["key1": "a", "key2": "b"]
Result: Optional(["key1": "a", "key2": "b"])
//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String]
type: String
value: {"key1": "a", "key2": "b"}
Result: Optional(["key1": "a", "key2": "b"])
//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String]
type: String
value: {"key1": "a", "key2": "b"}
Result: Optional(["key1": a, "key2": b])
//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String] with JSONSerialization error
type: String
value: {"key1": "a", "key2": "b"}
ERROR: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 21." UserInfo={NSDebugDescription=Invalid value around character 21.}

I found code which converts the json string to NSDictionary or NSArray. Just add the extension.
SWIFT 3.0
HOW TO USE
let jsonData = (convertedJsonString as! String).parseJSONString
EXTENSION
extension String
{
var parseJSONString: AnyObject?
{
let data = self.data(using: String.Encoding.utf8, allowLossyConversion: false)
if let jsonData = data
{
// Will return an object or nil if JSON decoding fails
do
{
let message = try JSONSerialization.jsonObject(with: jsonData, options:.mutableContainers)
if let jsonResult = message as? NSMutableArray {
return jsonResult //Will return the json array output
} else if let jsonResult = message as? NSMutableDictionary {
return jsonResult //Will return the json dictionary output
} else {
return nil
}
}
catch let error as NSError
{
print("An error occurred: \(error)")
return nil
}
}
else
{
// Lossless conversion of the string was not possible
return nil
}
}
}

In 2022 year, I'm using JSONDecoder.
struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}
let json = """
{
"name": "Durian",
"points": 600,
"description": "A fruit with a distinctive scent."
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
do {
let product = try decoder.decode(GroceryProduct.self, from: json)
}
catch { //error handle }
print(product.name) // Prints "Durian"

let JSONData = jsonString.data(using: .utf8)!
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
guard let userDictionary = jsonResult as? Dictionary<String, AnyObject> else {
throw NSError()}

Related

Swift read from JSON dictionary

I am sending an Alamofire request and inside of my completion handler I have:
if let jsonData = response.result.value {
result = jsonData
guard let data = result.data(using: .utf8) else { return}
guard let dictionary = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
print("Could not cast JSON content as a Dictionary<String, Any>")
return
}
print("dictionary: \(dictionary)")
if dictionary["status"] as! String == "false"{
//Do something
}
}
else{
result = "\(response.error)"
}
The result of printing dictionary is ["status":false, "value":A1]. Ultimately I want to use status for my if statement. However I get a crash on the if statement line: if dictionary["status"] as! String == "false" of Fatal error: Unexpectedly found nil while unwrapping an Optional value. I also tried changing the line to if dictionary["status"] as! Bool == false and I get the exact same error.
The json as returned from the request is:
{
"value": "A1",
"status": "false"
}
So my question is, what is the correct way to get the value for status out of dictionary?
Would something like this work?
struct jsonOut: Codable {
let value: String
let status: String
}
if let jsonData = response.result.value {
result = jsonData
guard let data = result.data(using: .utf8)
let status = try JSONDecoder().decode(jsonOut.self, from: data)
}
Since the JSON has the format:
{
"value": "A1",
"status": "false"
}
The correct way is using Codable with the same format as the JSON:
struct jsonOut: Codable {
let value: String
let status: String
}
if let jsonData = response.result.value {
result = jsonData
guard let data = result.data(using: .utf8)
let statusData = try JSONDecoder().decode(jsonOut.self, from: data)
print("status: \(statusData.status)"
}

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)
}

Convert Dictionary to JSON in Swift

I have create the next Dictionary:
var postJSON = [ids[0]:answersArray[0], ids[1]:answersArray[1], ids[2]:answersArray[2]] as Dictionary
and I get:
[2: B, 1: A, 3: C]
So, how can I convert it to JSON?
Swift 3.0
With Swift 3, the name of NSJSONSerialization and its methods have changed, according to the Swift API Design Guidelines.
let dic = ["2": "B", "1": "A", "3": "C"]
do {
let jsonData = try JSONSerialization.data(withJSONObject: dic, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// here "decoded" is of type `Any`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:String] {
// use dictFromJSON
}
} catch {
print(error.localizedDescription)
}
Swift 2.x
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(dic, options: NSJSONWritingOptions.PrettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// here "decoded" is of type `AnyObject`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:String] {
// use dictFromJSON
}
} catch let error as NSError {
print(error)
}
Swift 1
var error: NSError?
if let jsonData = NSJSONSerialization.dataWithJSONObject(dic, options: NSJSONWritingOptions.PrettyPrinted, error: &error) {
if error != nil {
println(error)
} else {
// here "jsonData" is the dictionary encoded in JSON data
}
}
if let decoded = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &error) as? [String:String] {
if error != nil {
println(error)
} else {
// here "decoded" is the dictionary decoded from JSON data
}
}
You are making a wrong assumption. Just because the debugger/Playground shows your dictionary in square brackets (which is how Cocoa displays dictionaries) that does not mean that is the way the JSON output is formatted.
Here is example code that will convert a dictionary of strings to JSON:
Swift 3 version:
import Foundation
let dictionary = ["aKey": "aValue", "anotherKey": "anotherValue"]
if let theJSONData = try? JSONSerialization.data(
withJSONObject: dictionary,
options: []) {
let theJSONText = String(data: theJSONData,
encoding: .ascii)
print("JSON string = \(theJSONText!)")
}
To display the above in "pretty printed" format you'd change the options line to:
options: [.prettyPrinted]
Or in Swift 2 syntax:
import Foundation
let dictionary = ["aKey": "aValue", "anotherKey": "anotherValue"]
let theJSONData = NSJSONSerialization.dataWithJSONObject(
dictionary ,
options: NSJSONWritingOptions(0),
error: nil)
let theJSONText = NSString(data: theJSONData!,
encoding: NSASCIIStringEncoding)
println("JSON string = \(theJSONText!)")
The output of that is
"JSON string = {"anotherKey":"anotherValue","aKey":"aValue"}"
Or in pretty format:
{
"anotherKey" : "anotherValue",
"aKey" : "aValue"
}
The dictionary is enclosed in curly braces in the JSON output, just as you'd expect.
EDIT:
In Swift 3/4 syntax, the code above looks like this:
let dictionary = ["aKey": "aValue", "anotherKey": "anotherValue"]
if let theJSONData = try? JSONSerialization.data(
withJSONObject: dictionary,
options: .prettyPrinted
),
let theJSONText = String(data: theJSONData,
encoding: String.Encoding.ascii) {
print("JSON string = \n\(theJSONText)")
}
}
Swift 5:
let dic = ["2": "B", "1": "A", "3": "C"]
let encoder = JSONEncoder()
if let jsonData = try? encoder.encode(dic) {
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
}
Note that keys and values must implement Codable. Strings, Ints, and Doubles (and more) are already Codable. See Encoding and Decoding Custom Types.
Also note that Any does not conform to Codable. It is likely still a good approach to adapt your data to become Codable so that you are making use of Swift typing (especially in the case that you are also going to decode any encoded json), and so that you can be more declarative about the outcome of your encoding.
My answer for your question is below
let dict = ["0": "ArrayObjectOne", "1": "ArrayObjecttwo", "2": "ArrayObjectThree"]
var error : NSError?
let jsonData = try! NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions.PrettyPrinted)
let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String
print(jsonString)
Answer is
{
"0" : "ArrayObjectOne",
"1" : "ArrayObjecttwo",
"2" : "ArrayObjectThree"
}
Swift 4 Dictionary extension.
extension Dictionary {
var jsonStringRepresentation: String? {
guard let theJSONData = try? JSONSerialization.data(withJSONObject: self,
options: [.prettyPrinted]) else {
return nil
}
return String(data: theJSONData, encoding: .ascii)
}
}
Sometimes it's necessary to print out server's response for debugging purposes. Here's a function I use:
extension Dictionary {
var json: String {
let invalidJson = "Not a valid JSON"
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String(bytes: jsonData, encoding: String.Encoding.utf8) ?? invalidJson
} catch {
return invalidJson
}
}
func printJson() {
print(json)
}
}
Example of use:
(lldb) po dictionary.printJson()
{
"InviteId" : 2,
"EventId" : 13591,
"Messages" : [
{
"SenderUserId" : 9514,
"MessageText" : "test",
"RecipientUserId" : 9470
},
{
"SenderUserId" : 9514,
"MessageText" : "test",
"RecipientUserId" : 9470
}
],
"TargetUserId" : 9470,
"InvitedUsers" : [
9470
],
"InvitingUserId" : 9514,
"WillGo" : true,
"DateCreated" : "2016-08-24 14:01:08 +00:00"
}
Swift 5:
extension Dictionary {
/// Convert Dictionary to JSON string
/// - Throws: exception if dictionary cannot be converted to JSON data or when data cannot be converted to UTF8 string
/// - Returns: JSON string
func toJson() throws -> String {
let data = try JSONSerialization.data(withJSONObject: self)
if let string = String(data: data, encoding: .utf8) {
return string
}
throw NSError(domain: "Dictionary", code: 1, userInfo: ["message": "Data cannot be converted to .utf8 string"])
}
}
Swift 3:
let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: [])
let jsonString = String(data: jsonData!, encoding: .utf8)!
print(jsonString)
In Swift 5.4
extension Dictionary {
var jsonData: Data? {
return try? JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted])
}
func toJSONString() -> String? {
if let jsonData = jsonData {
let jsonString = String(data: jsonData, encoding: .utf8)
return jsonString
}
return nil
}
}
The idea of having it as a variable is because then you can reuse it like that:
extension Dictionary {
func decode<T:Codable>() throws -> T {
return try JSONDecoder().decode(T.self, from: jsonData ?? Data())
}
}
Answer for your question is below:
Swift 2.1
do {
if let postData : NSData = try NSJSONSerialization.dataWithJSONObject(dictDataToBeConverted, options: NSJSONWritingOptions.PrettyPrinted){
let json = NSString(data: postData, encoding: NSUTF8StringEncoding)! as String
print(json)}
}
catch {
print(error)
}
Here's an easy extension to do this:
https://gist.github.com/stevenojo/0cb8afcba721838b8dcb115b846727c3
extension Dictionary {
func jsonString() -> NSString? {
let jsonData = try? JSONSerialization.data(withJSONObject: self, options: [])
guard jsonData != nil else {return nil}
let jsonString = String(data: jsonData!, encoding: .utf8)
guard jsonString != nil else {return nil}
return jsonString! as NSString
}
}
using lldb
(lldb) p JSONSerialization.data(withJSONObject: notification.request.content.userInfo, options: [])
(Data) $R16 = 375 bytes
(lldb) p String(data: $R16!, encoding: .utf8)!
(String) $R18 = "{\"aps\": \"some_text\"}"
//or
p String(data: JSONSerialization.data(withJSONObject: notification.request.content.userInfo, options: [])!, encoding: .utf8)!
(String) $R4 = "{\"aps\": \"some_text\"}"
do{
let dataDict = [ "level" :
[
["column" : 0,"down" : 0,"left" : 0,"right" : 0,"row" : 0,"up" : 0],
["column" : 1,"down" : 0,"left" : 0,"right" : 0,"row" : 0,"up" : 0],
["column" : 2,"down" : 0,"left" : 0,"right" : 0,"row" : 0,"up" : 0],
["column" : 0,"down" : 0,"left" : 0,"right" : 0,"row" : 1,"up" : 0],
["column" : 1,"down" : 0,"left" : 0,"right" : 0,"row" : 1,"up" : 0],
["column" : 2,"down" : 0,"left" : 0,"right" : 0,"row" : 1,"up" : 0]
]
]
var jsonData = try JSONSerialization.data(withJSONObject: dataDict, options: JSONSerialization.WritingOptions.prettyPrinted)
let jsonStringData = NSString(data: jsonData as Data, encoding: NSUTF8StringEncoding)! as String
print(jsonStringData)
}catch{
print(error.localizedDescription)
}
This works for me:
import SwiftyJSON
extension JSON {
mutating func appendIfKeyValuePair(key: String, value: Any){
if var dict = self.dictionaryObject {
dict[key] = value
self = JSON(dict)
}
}
}
Usage:
var data: JSON = []
data.appendIfKeyValuePair(key: "myKey", value: "myValue")
2022, swift 5
usage of extensions:
Encode:
if let json = statisticsDict.asJSONStr() {
//your action with json
}
Decode from Dictionary:
json.decodeFromJson(type: [String:AppStat].self)
.onSuccess{
$0// your dictionary of type: [String:AppStat]
}
extensions:
extension Dictionary where Key: Encodable, Value: Encodable {
func asJSONStr() -> String? {
let encoder = JSONEncoder()
if let jsonData = try? encoder.encode(self) {
if let jsonString = String(data: jsonData, encoding: .utf8) {
return jsonString
}
}
return nil
}
}
public extension String {
func decodeFromJson<T>(type: T.Type) -> Result<T, Error> where T: Decodable {
self.asData()
.flatMap { JSONDecoder().try(type, from: $0) }
}
func asData() -> Result<Data, Error> {
if let data = self.data(using: .utf8) {
return .success(data)
} else {
return .failure(WTF("can't convert string to data: \(self)"))
}
}
}
extension JSONDecoder {
func `try`<T: Decodable>(_ t: T.Type, from data: Data) -> Result<T,Error> {
do {
return .success(try self.decode(t, from: data))
} catch {
return .failure(error)
}
}
}
private func convertDictToJson(dict : NSDictionary) -> NSDictionary?
{
var jsonDict : NSDictionary!
do {
let jsonData = try JSONSerialization.data(withJSONObject:dict, options:[])
let jsonDataString = String(data: jsonData, encoding: String.Encoding.utf8)!
print("Post Request Params : \(jsonDataString)")
jsonDict = [ParameterKey : jsonDataString]
return jsonDict
} catch {
print("JSON serialization failed: \(error)")
jsonDict = nil
}
return jsonDict
}

Parse json in Swift, AnyObject type

I'm trying to parse a json but I have some difficulties with the data types and notably the AnyObject type + downcasting.
Let's consider the following json (it's an extract of a full json).
{ "weather":
[
{
"id":804,
"main":"Clouds",
"description":"overcast clouds",
"icon":"04d"
}
],
}
To me, the json can be described as follow :
- json: Dictionary of type [String: AnyObject] (or NSDictionary, so = [NSObject, AnyObject] in Xcode 6 b3)
- "weather": Array of type [AnyObject] (or NSArray)
- Dictionary of type [String: AnyObject] (or NSDictionary, so = [NSObject, AnyObject] in Xcode 6 b3)
My json is of type AnyObject! (I use JSONObjectWithData to get the JSON from a URL).
I then want to access the weather Dictionary. Here is the code I wrote.
var localError: NSError?
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &localError)
if let dict = json as? [String: AnyObject] {
if let weatherDictionary = dict["weather"] as? [AnyObject] {
// Do stuff with the weatherDictionary
}
}
Here is the error I got
Playground execution failed: error: <EXPR>:28:56: error: '[AnyObject]' is not a subtype of '(String, AnyObject)'
if let weatherDictionary = dict["weather"] as? [AnyObject] {
I don't understand why dict["weather"] is compared to a subtype of (String, AnyObject) and not AnyObject.
I declared my dictionary as [String: AnyObject], so I i access a value using the String key, I should have an AnyObject, no ?
If I use NSDictionary instead of [String: AnyObject], it works.
If I use NSArray instead of [AnyObject], it works.
- The Xcode 6 beta 3 release notes tell that "NSDictionary* is now imported from Objective-C APIs as [NSObject : AnyObject].".
- And the Swift book: "When you bridge from an NSArray object to a Swift array, the resulting array is of type [AnyObject]."
EDIT
I forgot to force unwrapping the dict["weather"]!.
if let dict = json as? [String: AnyObject] {
println(dict)
if let weatherDictionary = dict["weather"]! as? [AnyObject] {
println("\nWeather dictionary:\n\n\(weatherDictionary)")
if let descriptionString = weatherDictionary[0]["description"]! as? String {
println("\nDescription of the weather is: \(descriptionString)")
}
}
}
Note that we should double check for the existence of the first Optional.
if let dict = json as? [String: AnyObject] {
for key in ["weather", "traffic"] {
if let dictValue = dict[key] {
if let subArray = dictValue as? [AnyObject] {
println(subArray[0])
}
} else {
println("Key '\(key)' not found")
}
}
}
This works fine for me in the playground and in the terminal using env xcrun swift
UPDATED FOR SWIFT 4 AND CODABLE
Here is a Swift 4 example using the Codable protocol.
var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"
struct Weather: Codable {
let id: Int
let main: String
let description: String
let icon: String
}
struct Result: Codable {
let weather: [Weather]
}
do {
let weather = try JSONDecoder().decode(Result.self, from: jsonStr.data(using: .utf8)!)
print(weather)
}
catch {
print(error)
}
UPDATED FOR SWIFT 3.0
I have updated the code for Swift 3 and also showed how to wrap the parsed JSON into objects. Thanks for all the up votes!
import Foundation
struct Weather {
let id: Int
let main: String
let description: String
let icon: String
}
extension Weather {
init?(json: [String: Any]) {
guard
let id = json["id"] as? Int,
let main = json["main"] as? String,
let description = json["description"] as? String,
let icon = json["icon"] as? String
else { return nil }
self.id = id
self.main = main
self.description = description
self.icon = icon
}
}
var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"
enum JSONParseError: Error {
case notADictionary
case missingWeatherObjects
}
var data = jsonStr.data(using: String.Encoding.ascii, allowLossyConversion: false)
do {
var json = try JSONSerialization.jsonObject(with: data!, options: [])
guard let dict = json as? [String: Any] else { throw JSONParseError.notADictionary }
guard let weatherJSON = dict["weather"] as? [[String: Any]] else { throw JSONParseError.missingWeatherObjects }
let weather = weatherJSON.flatMap(Weather.init)
print(weather)
}
catch {
print(error)
}
-- Previous Answer --
import Foundation
var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"
var data = jsonStr.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
var localError: NSError?
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError)
if let dict = json as? [String: AnyObject] {
if let weather = dict["weather"] as? [AnyObject] {
for dict2 in weather {
let id = dict2["id"]
let main = dict2["main"]
let description = dict2["description"]
println(id)
println(main)
println(description)
}
}
}
Since I'm still getting up-votes for this answer, I figured I would revisit it for Swift 2.0:
import Foundation
var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"
var data = jsonStr.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
do {
var json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
if let dict = json as? [String: AnyObject] {
if let weather = dict["weather"] as? [AnyObject] {
for dict2 in weather {
let id = dict2["id"] as? Int
let main = dict2["main"] as? String
let description = dict2["description"] as? String
print(id)
print(main)
print(description)
}
}
}
}
catch {
print(error)
}
The biggest difference is that the variable json is no longer an optional type and the do/try/catch syntax. I also went ahead and typed id, main, and description.
Try:
https://github.com/dankogai/swift-json
With it you can go like this:
let obj:[String:AnyObject] = [
"array": [JSON.null, false, 0, "", [], [:]],
"object":[
"null": JSON.null,
"bool": true,
"int": 42,
"double": 3.141592653589793,
"string": "a α\t弾\n𪚲",
"array": [],
"object": [:]
],
"url":"http://blog.livedoor.com/dankogai/"
]
let json = JSON(obj)
json.toString()
json["object"]["null"].asNull // NSNull()
json["object"]["bool"].asBool // true
json["object"]["int"].asInt // 42
json["object"]["double"].asDouble // 3.141592653589793
json["object"]["string"].asString // "a α\t弾\n𪚲"
json["array"][0].asNull // NSNull()
json["array"][1].asBool // false
json["array"][2].asInt // 0
json["array"][3].asString // ""
Using my library (https://github.com/isair/JSONHelper) you can do this with your json variable of type AnyObject:
var weathers = [Weather]() // If deserialization fails, JSONHelper just keeps the old value in a non-optional variable. This lets you assign default values like this.
if let jsonDictionary = json as? JSONDictionary { // JSONDictionary is an alias for [String: AnyObject]
weathers <-- jsonDictionary["weather"]
}
Had your array not been under the key "weather", your code would have been just this:
var weathers = [Weather]()
weathers <-- json
Or if you have a json string in your hands you can just pass it as well, instead of creating a JSON dictionary from the string first. The only setup you need to do is writing a Weather class or struct:
struct Weather: Deserializable {
var id: String?
var name: String?
var description: String?
var icon: String?
init(data: [String: AnyObject]) {
id <-- data["id"]
name <-- data["name"]
description <-- data["description"]
icon <-- data["icon"]
}
}

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
}