How to serialize or convert Swift objects to JSON? - json

This below class
class User: NSManagedObject {
#NSManaged var id: Int
#NSManaged var name: String
}
Needs to be converted to
{
"id" : 98,
"name" : "Jon Doe"
}
I tried manually passing the object to a function which sets the variables into a dictionary and returns the dictionary. But I would want a better way to accomplish this.

In Swift 4, you can inherit from the Codable type.
struct Dog: Codable {
var name: String
var owner: String
}
// Encode
let dog = Dog(name: "Rex", owner: "Etgar")
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(dog)
let json = String(data: jsonData, encoding: String.Encoding.utf8)
// Decode
let jsonDecoder = JSONDecoder()
let secondDog = try jsonDecoder.decode(Dog.self, from: jsonData)

Along with Swift 4 (Foundation) now it is natively supported in both ways, JSON string to an object - an object to JSON string.
Please see Apple's documentation here JSONDecoder() and here JSONEncoder()
JSON String to Object
let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let myStruct = try! decoder.decode(myStruct.self, from: jsonData)
Swift Object to JSONString
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(myStruct)
print(String(data: data, encoding: .utf8)!)
You can find all details and examples here Ultimate Guide to JSON Parsing With Swift 4

UPDATE: Codable protocol introduced in Swift 4 should be sufficient for most of the JSON parsing cases. Below answer is for people who are stuck in previous versions of Swift and for legacy reasons
EVReflection :
This works of reflection principle. This takes less code and also supports NSDictionary, NSCoding, Printable, Hashable and Equatable
Example:
class User: EVObject { # extend EVObject method for the class
var id: Int = 0
var name: String = ""
var friends: [User]? = []
}
# use like below
let json:String = "{\"id\": 24, \"name\": \"Bob Jefferson\", \"friends\": [{\"id\": 29, \"name\": \"Jen Jackson\"}]}"
let user = User(json: json)
ObjectMapper :
Another way is by using ObjectMapper. This gives more control but also takes a lot more code.
Example:
class User: Mappable { # extend Mappable method for the class
var id: Int?
var name: String?
required init?(_ map: Map) {
}
func mapping(map: Map) { # write mapping code
name <- map["name"]
id <- map["id"]
}
}
# use like below
let json:String = "{\"id\": 24, \"name\": \"Bob Jefferson\", \"friends\": [{\"id\": 29, \"name\": \"Jen Jackson\"}]}"
let user = Mapper<User>().map(json)

I worked a bit on a smaller solution that doesn't require inheritance. But it hasn't been tested much. It's pretty ugly atm.
https://github.com/peheje/JsonSerializerSwift
You can pass it into a playground to test it. E.g. following class structure:
//Test nonsense data
class Nutrient {
var name = "VitaminD"
var amountUg = 4.2
var intArray = [1, 5, 9]
var stringArray = ["nutrients", "are", "important"]
}
class Fruit {
var name: String = "Apple"
var color: String? = nil
var weight: Double = 2.1
var diameter: Float = 4.3
var radius: Double? = nil
var isDelicious: Bool = true
var isRound: Bool? = nil
var nullString: String? = nil
var date = NSDate()
var optionalIntArray: Array<Int?> = [1, 5, 3, 4, nil, 6]
var doubleArray: Array<Double?> = [nil, 2.2, 3.3, 4.4]
var stringArray: Array<String> = ["one", "two", "three", "four"]
var optionalArray: Array<Int> = [2, 4, 1]
var nutrient = Nutrient()
}
var fruit = Fruit()
var json = JSONSerializer.toJson(fruit)
print(json)
prints
{"name": "Apple", "color": null, "weight": 2.1, "diameter": 4.3, "radius": null, "isDelicious": true, "isRound": null, "nullString": null, "date": "2015-06-19 22:39:20 +0000", "optionalIntArray": [1, 5, 3, 4, null, 6], "doubleArray": [null, 2.2, 3.3, 4.4], "stringArray": ["one", "two", "three", "four"], "optionalArray": [2, 4, 1], "nutrient": {"name": "VitaminD", "amountUg": 4.2, "intArray": [1, 5, 9], "stringArray": ["nutrients", "are", "important"]}}

This is not a perfect/automatic solution but I believe this is the idiomatic and native way to do such. This way you don't need any libraries or such.
Create an protocol such as:
/// A generic protocol for creating objects which can be converted to JSON
protocol JSONSerializable {
private var dict: [String: Any] { get }
}
extension JSONSerializable {
/// Converts a JSONSerializable conforming class to a JSON object.
func json() rethrows -> Data {
try JSONSerialization.data(withJSONObject: self.dict, options: nil)
}
}
Then implement it in your class such as:
class User: JSONSerializable {
var id: Int
var name: String
var dict { return ["id": self.id, "name": self.name] }
}
Now:
let user = User(...)
let json = user.json()
Note: if you want json as a string, it is very simply to convert to a string: String(data: json, encoding .utf8)

Some of the above answers are completely fine, but I added an extension here, just to make it much more readable and usable.
extension Encodable {
var convertToString: String? {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
do {
let jsonData = try jsonEncoder.encode(self)
return String(data: jsonData, encoding: .utf8)
} catch {
return nil
}
}
}
struct User: Codable {
var id: Int
var name: String
}
let user = User(id: 1, name: "name")
print(user.convertToString!)
//This will print like the following:
{
"id" : 1,
"name" : "name"
}

Not sure if lib/framework exists, but if you would like to do it automatically and you would like to avoid manual labour :-) stick with MirrorType ...
class U {
var id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
extension U {
func JSONDictionary() -> Dictionary<String, Any> {
var dict = Dictionary<String, Any>()
let mirror = reflect(self)
var i: Int
for i = 0 ; i < mirror.count ; i++ {
let (childName, childMirror) = mirror[i]
// Just an example how to check type
if childMirror.valueType is String.Type {
dict[childName] = childMirror.value
} else if childMirror.valueType is Int.Type {
// Convert to NSNumber for example
dict[childName] = childMirror.value
}
}
return dict
}
}
Take it as a rough example, lacks proper conversion support, lacks recursion, ... It's just MirrorType demonstration ...
P.S. Here it's done in U, but you're going to enhance NSManagedObject and then you'll be able to convert all NSManagedObject subclasses. No need to implement this in all subclasses/managed objects.

struct User:Codable{
var id:String?
var name:String?
init(_ id:String,_ name:String){
self.id = id
self.name = name
}
}
Now just make your object like this
let user = User("1","pawan")
do{
let userJson = try JSONEncoder().encode(parentMessage)
}catch{
fatalError("Unable To Convert in Json")
}
Then reconvert from json to Object
let jsonDecoder = JSONDecoder()
do{
let convertedUser = try jsonDecoder.decode(User.self, from: userJson.data(using: .utf8)!)
}catch{
}

2021 | SWIFT 5.1 | Results solution
Input data:
struct ConfigCreds: Codable {
// some params
}
usage:
// get JSON from Object
configCreds
.asJson()
.onSuccess{ varToSaveJson = $0 }
.onFailure{ _ in // any failure code }
// get object of type "ConfigCreds" from JSON
someJsonString
.decodeFromJson(type: ConfigCreds.self)
.onSuccess { configCreds = $0 }
.onFailure{ _ in // any failure code }
Back code:
#available(macOS 10.15, *)
public extension Encodable {
func asJson() -> Result<String, Error>{
JSONEncoder()
.try(self)
.flatMap{ $0.asString() }
}
}
public extension String {
func decodeFromJson<T>(type: T.Type) -> Result<T, Error> where T: Decodable {
self.asData()
.flatMap { JSONDecoder().try(type, from: $0) }
}
}
///////////////////////////////
/// HELPERS
//////////////////////////////
#available(macOS 10.15, *)
fileprivate extension JSONEncoder {
func `try`<T : Encodable>(_ value: T) -> Result<Output, Error> {
do {
return .success(try self.encode(value))
} catch {
return .failure(error)
}
}
}
fileprivate 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)
}
}
}
fileprivate extension String {
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)"))
}
}
}
fileprivate extension Data {
func asString() -> Result<String, Error> {
if let str = String(data: self, encoding: .utf8) {
return .success(str)
} else {
return .failure(WTF("can't convert Data to string"))
}
}
}
fileprivate func WTF(_ msg: String, code: Int = 0) -> Error {
NSError(code: code, message: msg)
}

Related

How convert data to Json in Realm version 10.15.0

in the realm version 10.7.1 it used this code but when migrating to 10.15.0 it is no longer possible because the ListBase class no longer exists
extension Object {
func toWebServiceRequest() -> [String:AnyObject] {
let properties = self.objectSchema.properties.map { $0.name }
var dicProps = [String:AnyObject]()
for (key, value) in self.dictionaryWithValues(forKeys: properties) {
if let value = value as? ListBase {
dicProps[key] = value.toArray() as AnyObject
} else if let value = value as? Object {
dicProps[key] = value.toWebServiceRequest() as AnyObject
} else {
dicProps[key] = value as AnyObject
}
}
return dicProps
}
}
extension ListBase {
func toArray() -> [AnyObject] {
var _toArray = [AnyObject]()
for i in 0..<self._rlmArray.count {
let obj = unsafeBitCast(self._rlmArray[i], to: Object.self)
_toArray.append(obj.toWebServiceRequest() as AnyObject)
}
return _toArray
}
}
The mongo team wants you to use the Codable protocol rather than some custom encoding mechanism. This is really the proper way to do it, otherwise you have to rely on Realm implementation details.
class Parent: Object, Codable {
#Persisted var name: String = ""
#Persisted var children: List<Child>
convenience init(name: String, children: [Child] = []) {
self.init()
self.name = name
self.children.append(objectsIn: children)
}
}
If the server is expecting keys different from your object's property names you can define custom CodingKeys:
class Child: Object, Codable {
#Persisted var id: Int = 0
#Persisted var name: String = ""
enum CodingKeys: String, CodingKey {
case id = "_id"
case name = "name"
}
convenience init(id: Int, name: String) {
self.init()
self.id = id
self.name = name
}
}
Usage is simple:
func makeParent() -> Parent {
Parent(name: "Alex", children: [
Child(id: 1, name: "Jim"),
Child(id: 2, name: "Tom"),
Child(id: 3, name: "Sam"),
Child(id: 4, name: "Joe"),
])
}
func encode<Item: Encodable>(_ item: Item) throws -> Data {
let encoder = JSONEncoder()
return try encoder.encode(item)
}
func convertDataToString(_ data: Data) throws -> String? {
return String(data: data, encoding: .utf8)
}
func decode<Item: Decodable>(_ type: Item.Type, from data: Data) throws -> Item {
let decoder = JSONDecoder()
return try decoder.decode(type, from: data)
}
func executeProgram() throws {
let parent = makeParent()
let encodedJSONData = try encode(parent)
guard let encodedJSONString = try convertDataToString(encodedJSONData) else {
throw Error.badJSON
}
print("JSON: \(encodedJSONString)")
let decodedParent = try decode(Parent.self, from: encodedJSONData)
print("Decoded Parent: \(decodedParent)")
}
On the encode route this results in:
JSON:
{"name":"Alex","children":[{"_id":1,"name":"Jim"},{"_id":2,"name":"Tom"},{"_id":3,"name":"Sam"},{"_id":4,"name":"Joe"}]}
And on the decode route this results in:
Decoded Parent: Parent { name = Alex; children = List
<0x600000a2b800> ( [0] Child { id = 1; name = Jim; }, [1]
Child { id = 2; name = Tom; }, [2] Child { id = 3;
name = Sam; }, [3] Child { id = 4; name = Joe; } ); }
You can obviously omit the String conversion just work with the resulting Data.

How can I easily see the JSON output from my objects that conform to the `Codable` Protocol

I deal with lots of objects that I serialize/deserialize to JSON using the Codable protocol.
It isn't that hard to create a JSONEncoder, set it up to pretty-print, convert the object to JSON, and then convert that to a string, but seems like a lot of work. Is there a simple way to say "please show me the JSON output for this object?"
EDIT:
Say for example I have the following structures:
struct Foo: Codable {
let string1: String?
let string2: String?
let date: Date
let val: Int
let aBar: Bar
}
struct Bar: Codable {
let name: String
}
And say I've created a Foo object:
let aBar = Bar(name: "Fred")
let aFoo = Foo(string1: "string1", string2: "string2", date: Date(), val: 42, aBar: aBar)
I could print that with a half-dozen lines of custom code:
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let data = try? encoder.encode(aFoo),
let output = String(data: data, encoding: .utf8)
else { fatalError( "Error converting \(aFoo) to JSON string") }
print("JSON string = \(output)")
Which would give the output:
JSON string = {
"date" : 557547327.56354201,
"aBar" : {
"name" : "Fred"
},
"string1" : "string1",
"val" : 42,
"string2" : "string2"
}
I get tired of writing the same half-dozen lines of code each time I need it. Is there an easier way?
I would recommend creating a static encoder so you don't create a new encoder every time you call that property:
extension JSONEncoder {
static let shared = JSONEncoder()
static let iso8601 = JSONEncoder(dateEncodingStrategy: .iso8601)
static let iso8601PrettyPrinted = JSONEncoder(dateEncodingStrategy: .iso8601, outputFormatting: .prettyPrinted)
}
extension JSONEncoder {
convenience init(dateEncodingStrategy: DateEncodingStrategy,
outputFormatting: OutputFormatting = [],
keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys) {
self.init()
self.dateEncodingStrategy = dateEncodingStrategy
self.outputFormatting = outputFormatting
self.keyEncodingStrategy = keyEncodingStrategy
}
}
Considering that you are calling this method inside a Encodable extension you can just force try!. You can also force the conversion from data to string:
extension Encodable {
func data(using encoder: JSONEncoder = .iso8601) throws -> Data {
try encoder.encode(self)
}
func dataPrettyPrinted() throws -> Data {
try JSONEncoder.iso8601PrettyPrinted.encode(self)
}
// edit if you need the data using a custom date formatter
func dataDateFormatted(with dateFormatter: DateFormatter) throws -> Data {
JSONEncoder.shared.dateEncodingStrategy = .formatted(dateFormatter)
return try JSONEncoder.shared.encode(self)
}
func json() throws -> String {
String(data: try data(), encoding: .utf8) ?? ""
}
func jsonPrettyPrinted() throws -> String {
String(data: try dataPrettyPrinted(), encoding: .utf8) ?? ""
}
func jsonDateFormatted(with dateFormatter: DateFormatter) throws -> String {
return String(data: try dataDateFormatted(with: dateFormatter), encoding: .utf8) ?? ""
}
}
Playground testing
struct Foo: Codable {
let string1: String
let string2: String
let date: Date
let val: Int
let bar: Bar
}
struct Bar: Codable {
let name: String
}
let bar = Bar(name: "Fred")
let foo = Foo(string1: "string1", string2: "string2", date: Date(), val: 42, bar: bar)
try! print("JSON\n=================\n", foo.json(), terminator: "\n\n")
try! print("JSONPrettyPrinted\n=================\n", foo.jsonPrettyPrinted(), terminator: "\n\n")
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
try! print("JSONDateFormatted\n=================\n", foo.jsonDateFormatted(with: dateFormatter))
This will print
JSON
=================
{"date":"2020-11-06T20:22:55Z","bar":{"name":"Fred"},"string1":"string1","val":42,"string2":"string2"}
JSONPrettyPrinted
=================
{
"date" : "2020-11-06T20:22:55Z",
"bar" : {
"name" : "Fred"
},
"string1" : "string1",
"val" : 42,
"string2" : "string2"
}
JSONDateFormatted
=================
{"date":"6 November 2020","bar":{"name":"Fred"},"string1":"string1","val":42,"string2":"string2"}
There isn't a stock way to convert a Codable object graph to a "pretty" JSON string, but it's pretty easy to define a protocol to do it so you don't write the same conversion code over and over.
You can simply create an extension to the Encodable protocol, like this:
extension Encodable {
var prettyJSON: String {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let data = try? encoder.encode(self),
let output = String(data: data, encoding: .utf8)
else { return "Error converting \(self) to JSON string" }
return output
}
}
Then for any JSON object
print(myJSONobject.prettyJSON)
and it displays the JSON text in "pretty printed" form.
One thing the above won't do is support custom formatting of dates. To do that we can modify prettyJSON to be a function rather than a computed property, where it takes an optional DateFormatter as a parameter with a default value of nil.
extension Encodable {
func prettyJSON(formatter: DateFormatter? = nil) -> String {
let encoder = JSONEncoder()
if let formatter = formatter {
encoder.dateEncodingStrategy = .formatted(formatter)
}
encoder.outputFormatting = .prettyPrinted
guard let data = try? encoder.encode(self),
let output = String(data: data, encoding: .utf8)
else { return "Error converting \(self) to JSON string" }
return output
}
}
Then you can use it just like the above, except that you need to add parentheses after prettyJSON, e.g.
print(myJSONobject.prettyJSON())
That form ignores the new DateFormatter parameter, and will output the same JSON string as the above. However,If you have a custom date formatter:
var formatter = DateFormatter()
formatter.dateFormat = "MM-dd-yyyy HH:mm:ss"
print(myJSONobject.prettyJSON(formatter: formatter))
Then the dates in your object graph will be formatted using the specified DateFormatter

How to parse JSON with custom parameters using Codable protocol

I have a JSON with keys
{
"yearOfManufacture":"20/9/2018",
"carSize":8,
"isNew":true,
"carAssets":[
{
"color":"5761807993001",
"nativeId":"{\"app\":\"1234/Car/Native_App\",\"web\":\" /8888/Car/Native_Car_Desktop\"}"
}
]
}
I am trying to parse using Codable protocol with struct models
struct Cars: Codable {
var yearOfManufacture: String?
var carSize: Int = 0
var isNew: Bool = true
var carAssets: [CarAssests]?
}
struct CarAssests: Codable {
var color: String?
var nativeId: String?
}
I am getting error like The data couldn’t be read because it isn’t in the correct format. I tried using CodingKeys with decoder container not getting the exact type of "nativeId": "{\"app\":\"1234/Car/Native_App\",\"web\":\" /8888/Car/Native_Car_Desktop\"}" not getting exact data type of this.
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = .deferredToData
if let jsonData = jsonString.data(using: .utf8) {
do {
print(jsonData)
let assets = try decoder.decode(Cars.self, from: jsonData)
print(assets)
} catch {
print(error.localizedDescription)
}
}
I bet you are doing something like this:
let jsonString = """
{
"yearOfManufacture": "20/9/2018",
"carSize": 8,
"isNew": true,
"carAssets": [
{
"color": "5761807993001",
"nativeId": "{\"app\":\"1234/Car/Native_App\",\"web\":\" /8888/Car/Native_Car_Desktop\"}"
}
]
}
"""
In a multiline string, both \" and " mean the character ". So you have to write \\" to get the two characters \ and ":
let jsonString = """
{
"yearOfManufacture": "20/9/2018",
"carSize": 8,
"isNew": true,
"carAssets": [
{
"color": "5761807993001",
"nativeId": "{\\"app\\":\\"1234/Car/Native_App\\",\\"web\\":\\" /8888/Car/Native_Car_Desktop\\"}"
}
]
}
"""

Create JSON in swift

I need to create JSON like this:
Order = { type_id:'1',model_id:'1',
transfer:{
startDate:'10/04/2015 12:45',
endDate:'10/04/2015 16:00',
startPoint:'Ул. Момышулы, 45',
endPoint:'Аэропорт Астаны'
},
hourly:{
startDate:'10/04/2015',
endDate:'11/04/2015',
startPoint:'ЖД Вокзал',
endPoint:'',
undefined_time:'1'
},
custom:{
startDate:'12/04/2015',
endDate:'12/04/2015',
startPoint:'Астана',
endPoint:'Павлодар',
customPrice:'50 000'
},
commentText:'',
device_type:'ios'
};
The problem is that I can not create valid JSON.
Here is how I create object:
let jsonObject: [AnyObject] = [
["type_id": singleStructDataOfCar.typeID, "model_id": singleStructDataOfCar.modelID, "transfer": savedDataTransfer, "hourly": savedDataHourly, "custom": savedDataReis, "device_type":"ios"]
]
where savedData are dictionaries:
let savedData: NSDictionary = ["ServiceDataStartDate": singleStructdata.startofWork,
"ServiceDataAddressOfReq": singleStructdata.addressOfRequest,
"ServiceDataAddressOfDel": singleStructdata.addressOfDelivery,
"ServiceDataDetailedText": singleStructdata.detailedText, "ServiceDataPrice": singleStructdata.priceProposed]
When I use only strings creating my JSON object everything works fine. However when I include dictionaries NSJSONSerialization.isValidJSONObject(value) returns false. How can I create a valid dictionary?
One problem is that this code is not of type Dictionary.
let jsonObject: [Any] = [
[
"type_id": singleStructDataOfCar.typeID,
"model_id": singleStructDataOfCar.modelID,
"transfer": savedDataTransfer,
"hourly": savedDataHourly,
"custom": savedDataReis,
"device_type":"iOS"
]
]
The above is an Array of AnyObject with a Dictionary of type [String: AnyObject] inside of it.
Try something like this to match the JSON you provided above:
let savedData = ["Something": 1]
let jsonObject: [String: Any] = [
"type_id": 1,
"model_id": 1,
"transfer": [
"startDate": "10/04/2015 12:45",
"endDate": "10/04/2015 16:00"
],
"custom": savedData
]
let valid = JSONSerialization.isValidJSONObject(jsonObject) // true
For Swift 3.0, as of December 2016, this is how it worked for me:
let jsonObject: NSMutableDictionary = NSMutableDictionary()
jsonObject.setValue(value1, forKey: "b")
jsonObject.setValue(value2, forKey: "p")
jsonObject.setValue(value3, forKey: "o")
jsonObject.setValue(value4, forKey: "s")
jsonObject.setValue(value5, forKey: "r")
let jsonData: NSData
do {
jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: JSONSerialization.WritingOptions()) as NSData
let jsonString = NSString(data: jsonData as Data, encoding: String.Encoding.utf8.rawValue) as! String
print("json string = \(jsonString)")
} catch _ {
print ("JSON Failure")
}
EDIT 2018: I now use SwiftyJSON library to save time and make my development life easier and better. Dealing with JSON natively in Swift is an unnecessary headache and pain, plus wastes too much time, and creates code which is hard to read and write, and hence prone to lots of errors.
Creating a JSON String:
let para:NSMutableDictionary = NSMutableDictionary()
para.setValue("bidder", forKey: "username")
para.setValue("day303", forKey: "password")
para.setValue("authetication", forKey: "action")
let jsonData = try! NSJSONSerialization.dataWithJSONObject(para, options: NSJSONWritingOptions.allZeros)
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding) as! String
print(jsonString)
• Swift 4.1, April 2018
Here is a more general approach that can be used to create a JSON string by using values from a dictionary:
struct JSONStringEncoder {
/**
Encodes a dictionary into a JSON string.
- parameter dictionary: Dictionary to use to encode JSON string.
- returns: A JSON string. `nil`, when encoding failed.
*/
func encode(_ dictionary: [String: Any]) -> String? {
guard JSONSerialization.isValidJSONObject(dictionary) else {
assertionFailure("Invalid json object received.")
return nil
}
let jsonObject: NSMutableDictionary = NSMutableDictionary()
let jsonData: Data
dictionary.forEach { (arg) in
jsonObject.setValue(arg.value, forKey: arg.key)
}
do {
jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
} catch {
assertionFailure("JSON data creation failed with error: \(error).")
return nil
}
guard let jsonString = String.init(data: jsonData, encoding: String.Encoding.utf8) else {
assertionFailure("JSON string creation failed.")
return nil
}
print("JSON string: \(jsonString)")
return jsonString
}
}
How to use it:
let exampleDict: [String: Any] = [
"Key1" : "stringValue", // type: String
"Key2" : boolValue, // type: Bool
"Key3" : intValue, // type: Int
"Key4" : customTypeInstance, // type: e.g. struct Person: Codable {...}
"Key5" : customClassInstance, // type: e.g. class Human: NSObject, NSCoding {...}
// ...
]
if let jsonString = JSONStringEncoder().encode(exampleDict) {
// Successfully created JSON string.
// ...
} else {
// Failed creating JSON string.
// ...
}
Note: If you are adding instances of your custom types (structs) into the dictionary make sure your types conform to the Codable protocol and if you are adding objects of your custom classes into the dictionary make sure your classes inherit from NSObject and conform to the NSCoding protocol.
This worked for me... Swift 2
static func checkUsernameAndPassword(username: String, password: String) -> String?{
let para:NSMutableDictionary = NSMutableDictionary()
para.setValue("demo", forKey: "username")
para.setValue("demo", forKey: "password")
// let jsonError: NSError?
let jsonData: NSData
do{
jsonData = try NSJSONSerialization.dataWithJSONObject(para, options: NSJSONWritingOptions())
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding) as! String
print("json string = \(jsonString)")
return jsonString
} catch _ {
print ("UH OOO")
return nil
}
}
Swift 5 - 6/30/21
Adopt the Codable protocol (similar to interface in other programming languages)
struct ConfigRequestBody: Codable {
var systemid: String
var password: String
var request: String = "getconfig"
init(systemID: String, password: String){
self.systemid = systemID
self.password = password
}
}
Create instance of struct/class that you want to turn into JSON:
let requestBody = ConfigRequestBody(systemID: systemID, password: password)
Encode the object into JSON using a JSONEncoder. Here I print the string representation so you can see the result:
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let result = try encoder.encode(requestBody)
// RESULT IS NOW JSON-LIKE DATA OBJECT
if let jsonString = String(data: result, encoding: .utf8){
// JSON STRING
print("JSON \(jsonString)")
}
} catch {
print("Your parsing sucks \(error)")
return nil
}
}
Check out https://github.com/peheje/JsonSerializerSwift
Use case:
//Arrange your model classes
class Object {
var id: Int = 182371823
}
class Animal: Object {
var weight: Double = 2.5
var age: Int = 2
var name: String? = "An animal"
}
class Cat: Animal {
var fur: Bool = true
}
let m = Cat()
//Act
let json = JSONSerializer.toJson(m)
//Assert
let expected = "{\"fur\": true, \"weight\": 2.5, \"age\": 2, \"name\": \"An animal\", \"id\": 182371823}"
stringCompareHelper(json, expected) //returns true
Currently supports standard types, optional standard types, arrays, arrays of nullables standard types, array of custom classes, inheritance, composition of custom objects.
Use SwiftyJSON to generate JSON string. Learn from the comments of the answer of Matt.
#2022/10
let savedData = ["Something": 1]
var json = JSON()
json.dictionaryObject = [
"type_id": 1,
"model_id": 1,
"transfer": [
"startDate": "10/04/2015 12:45",
"endDate": "10/04/2015 16:00"
],
"custom": savedData
]
let jsonStr = json.rawString()

Deserialize JSON / NSDictionary to Swift objects

Is there a way to properly deserialize a JSON response to Swift objects resp. using DTOs as containers for fixed JSON APIs?
Something similar to http://james.newtonking.com/json or something like this example from Java
User user = jsonResponse.readEntity(User.class);
whereby jsonResponse.toString() is something like
{
"name": "myUser",
"email": "user#example.com",
"password": "passwordHash"
}
SWIFT 4 Update
Since you give a very simple JSON object the code prepared for to handle that model. If you need more complicated JSON models you need to improve this sample.
Your Custom Object
class Person : NSObject {
var name : String = ""
var email : String = ""
var password : String = ""
init(JSONString: String) {
super.init()
var error : NSError?
let JSONData = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let JSONDictionary: Dictionary = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &error) as NSDictionary
// Loop
for (key, value) in JSONDictionary {
let keyName = key as String
let keyValue: String = value as String
// If property exists
if (self.respondsToSelector(NSSelectorFromString(keyName))) {
self.setValue(keyValue, forKey: keyName)
}
}
// Or you can do it with using
// self.setValuesForKeysWithDictionary(JSONDictionary)
// instead of loop method above
}
}
And this is how you invoke your custom class with JSON string.
override func viewDidLoad() {
super.viewDidLoad()
let jsonString = "{ \"name\":\"myUser\", \"email\":\"user#example.com\", \"password\":\"passwordHash\" }"
var aPerson : Person = Person(JSONString: jsonString)
println(aPerson.name) // Output is "myUser"
}
I recommend that you use code generation (http://www.json4swift.com) to create native models out of the json response, this will save your time of parsing by hand and reduce the risk of errors due to mistaken keys, all elements will be accessible by model properties, this will be purely native and the models will make more sense rather checking the keys.
Your conversion will be as simple as:
let userObject = UserClass(userDictionary)
print(userObject!.name)
Swift 2: I really like the previous post of Mohacs! To make it more object oriented, i wrote a matching Extension:
extension NSObject{
convenience init(jsonStr:String) {
self.init()
if let jsonData = jsonStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
{
do {
let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String: AnyObject]
// Loop
for (key, value) in json {
let keyName = key as String
let keyValue: String = value as! String
// If property exists
if (self.respondsToSelector(NSSelectorFromString(keyName))) {
self.setValue(keyValue, forKey: keyName)
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
else
{
print("json is of wrong format!")
}
}
}
custom classes:
class Person : NSObject {
var name : String?
var email : String?
var password : String?
}
class Address : NSObject {
var city : String?
var zip : String?
}
invoking custom classes with JSON string:
var jsonString = "{ \"name\":\"myUser\", \"email\":\"user#example.com\", \"password\":\"passwordHash\" }"
let aPerson = Person(jsonStr: jsonString)
print(aPerson.name!) // Output is "myUser"
jsonString = "{ \"city\":\"Berlin\", \"zip\":\"12345\" }"
let aAddress = Address(jsonStr: jsonString)
print(aAddress.city!) // Output is "Berlin"
Yet another JSON handler I wrote:
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 // ""
As you see no !? needed between subscripts.
In addition to that you can apply your own schema like this:
//// schema by subclassing
class MyJSON : JSON {
override init(_ obj:AnyObject){ super.init(obj) }
override init(_ json:JSON) { super.init(json) }
var null :NSNull? { return self["null"].asNull }
var bool :Bool? { return self["bool"].asBool }
var int :Int? { return self["int"].asInt }
var double:Double? { return self["double"].asDouble }
var string:String? { return self["string"].asString }
var url: String? { return self["url"].asString }
var array :MyJSON { return MyJSON(self["array"]) }
var object:MyJSON { return MyJSON(self["object"]) }
}
let myjson = MyJSON(obj)
myjson.object.null // NSNull?
myjson.object.bool // Bool?
myjson.object.int // Int?
myjson.object.double // Double?
myjson.object.string // String?
myjson.url // String?
There's a great example by Apple for deserializing JSON with Swift 2.0
The trick is to use the guard keyword and chain the assignments like so:
init?(attributes: [String : AnyObject]) {
guard let name = attributes["name"] as? String,
let coordinates = attributes["coordinates"] as? [String: Double],
let latitude = coordinates["lat"],
let longitude = coordinates["lng"],
else {
return nil
}
self.name = name
self.coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
I personally prefer native parsing vs any 3rd party, as it is transparent and magic-less. (and bug less?)
Using quicktype, I generated your model and serialization helpers from your sample:
import Foundation
struct User: Codable {
let name: String
let email: String
let password: String
}
extension User {
static func from(json: String, using encoding: String.Encoding = .utf8) -> OtherUser? {
guard let data = json.data(using: encoding) else { return nil }
return OtherUser.from(data: data)
}
static func from(data: Data) -> OtherUser? {
let decoder = JSONDecoder()
return try? decoder.decode(OtherUser.self, from: data)
}
var jsonData: Data? {
let encoder = JSONEncoder()
return try? encoder.encode(self)
}
var jsonString: String? {
guard let data = self.jsonData else { return nil }
return String(data: data, encoding: .utf8)
}
}
Then parse User values like this:
let user = User.from(json: """{
"name": "myUser",
"email": "user#example.com",
"password": "passwordHash"
}""")!
I wrote this small open-source library recently that lets you quickly and easily deserialize dictionaries into Swift objects: https://github.com/isair/JSONHelper
Using it, deserializing data becomes as easy as this:
var myInstance = MyClass(data: jsonDictionary)
or
myInstance <-- jsonDictionary
And models need to look only like this:
struct SomeObjectType: Deserializable {
var someProperty: Int?
var someOtherProperty: AnotherObjectType?
var yetAnotherProperty: [YetAnotherObjectType]?
init(data: [String: AnyObject]) {
someProperty <-- data["some_key"]
someOtherProperty <-- data["some_other_key"]
yetAnotherProperty <-- data["yet_another_key"]
}
}
Which, in your case, would be:
struct Person: Deserializable {
var name: String?
var email: String?
var password: String?
init(data: [String: AnyObject]) {
name <-- data["name"]
email <-- data["email"]
password <-- data["password"]
}
}
If you would like parse from and to json without the need to manually map keys and fields, then you could also use EVReflection. You can then use code like:
var user:User = User(json:jsonString)
or
var jsonString:String = user.toJsonString()
The only thing you need to do is to use EVObject as your data objects base class.
See the GitHub page for more detailed sample code
I am expanding upon Mohacs and Peter Kreinz's excellent answers just a bit to cover the array of like objects case where each object contains a mixture of valid JSON data types. If the JSON data one is parsing is an array of like objects containing a mixture of JSON data types, the do loop for parsing the JSON data becomes this.
// Array of parsed objects
var parsedObjects = [ParsedObject]()
do {
let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as [Dictionary<String, AnyObject>]
// Loop through objects
for dict in json {
// ParsedObject is a single instance of an object inside the JSON data
// Its properties are a mixture of String, Int, Double and Bool
let parsedObject = ParsedObject()
// Loop through key/values in object parsed from JSON
for (key, value) in json {
// If property exists, set the value
if (parsedObject.respondsToSelector(NSSelectorFromString(keyName))) {
// setValue can handle AnyObject when assigning property value
parsedObject.setValue(keyValue, forKey: keyName)
}
}
parsedObjects.append(parsedObject)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
This way lets you get the user from a URL. It's parse the NSData to a NSDictionary and then to your NSObject.
let urlS = "http://api.localhost:3000/"
func getUser(username: Strung) -> User {
var user = User()
let url = NSURL(string: "\(urlS)\(username)")
if let data = NSData(contentsOfURL: url!) {
setKeysAndValues(user, dictionary: parseData(data))
}
return user
}
func setKeysAndValues (object : AnyObject, dictionary : NSDictionary) -> AnyObject {
for (key, value) in dictionary {
if let key = key as? String, let value = value as? String {
if (object.respondsToSelector(NSSelectorFromString(key))) {
object.setValue(value, forKey: key)
}
}
}
return object
}
func parseData (data : NSData) -> NSDictionary {
var error: NSError?
return NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
}
In Swift 4, You can use the Decoding, CodingKey protocols to deserialize the JSON response:
Create the class which confirm the decodable protocol
class UserInfo: Decodable
Create members of the class
var name: String
var email: String
var password: String
Create JSON key enum which inherits from CodingKey
enum UserInfoCodingKey: String, CodingKey {
case name
case password
case emailId
}
Implement init
required init(from decoder: Decoder) throws
The whole class look like :
Call Decoder
// jsonData is JSON response and we get the userInfo object
let userInfo = try JsonDecoder().decode(UserInfo.self, from: jsonData)
You do this by using NSJSONSerialization. Where data is your JSON.
First wrap it in an if statement to provide some error handling capablity
if let data = data,
json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] {
// Do stuff
} else {
// Do stuff
print("No Data :/")
}
then assign them:
let email = json["email"] as? String
let name = json["name"] as? String
let password = json["password"] as? String
Now, This will show you the result:
print("Found User iname: \(name) with email: \(email) and pass \(password)")
Taken from this Swift Parse JSON tutorial. You should check out the tutorial as it goes a lot more in depth and covers better error handling.