Reputation: 5974
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.
Upvotes: 137
Views: 173174
Reputation: 13429
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)!)
Upvotes: 56
Reputation: 11426
Data sample:
struct ConfigCreds: Codable { // Codable is important!
// some params
}
Solution usage sample:
var configCreds = ConfigCreds()
var jsonStr: String = ""
// Object -> JSON
configCreds
.asJson()
.onSuccess { jsonStr = $0 }
.onFailure { _ in // any failure code }
// JSON -> object of type "ConfigCreds"
someJsonString
.decodeFromJson(type: ConfigCreds.self)
.onSuccess { configCreds = $0 }
.onFailure { _ in // any failure code }
Back-End 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)
}
internal extension NSError {
convenience init(code: Int, message: String) {
let userInfo: [String: String] = [NSLocalizedDescriptionKey:message]
self.init(domain: "FTW", code: code, userInfo: userInfo)
}
}
Upvotes: 4
Reputation: 5924
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)
Upvotes: 212
Reputation: 704
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{
}
Upvotes: 4
Reputation: 329
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"
}
Upvotes: 8
Reputation: 5974
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
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)
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)
Upvotes: 28
Reputation: 14361
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)
Upvotes: 10
Reputation: 14204
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"]}}
Upvotes: 15
Reputation: 21229
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.
Upvotes: 2