Skip to content

Instantly share code, notes, and snippets.

@turowicz
Last active February 21, 2017 07:00
Show Gist options
  • Save turowicz/e7746a9c035356f9483d to your computer and use it in GitHub Desktop.
Save turowicz/e7746a9c035356f9483d to your computer and use it in GitHub Desktop.
Apple Swift strong type object serialization to JSON
import XCTest
class Person:Serializable{
var Name : String
var Surname : String
var Animals : Array<Animal>
init(Name:String, Surname:String) {
self.Name = Name
self.Surname = Surname
self.Animals = Array<Animal>()
}
}
class Animal:Serializable {
var Nickname : String
var Kind : String
init(Nickname : String, Kind : String) {
self.Nickname = Nickname
self.Kind = Kind
}
}
class SerializationTests: XCTestCase {
func test_serialization_works() {
var john = Person(Name: "John", Surname: "Doe")
john.Animals.append(Animal(Nickname: "Fluffy", Kind: "Dog"))
john.Animals.append(Animal(Nickname: "Purry", Kind: "Cat"))
println(john.toJson()) //will give binary data to include in HTTP Body
println(john.toJsonString()) //will give the exact string in JSON
//{"Surname":"Doe","Name":"John","Animals":[{"Kind":"Dog","Nickname":"Fluffy"},{"Kind":"Cat","Nickname":"Purry"}]}
var expected = "{\"Surname\":\"Doe\",\"Name\":\"John\",\"Animals\":[{\"Kind\":\"Dog\",\"Nickname\":\"Fluffy\"},{\"Kind\":\"Cat\",\"Nickname\":\"Purry\"}]}";
XCTAssertEqual(john.toJsonString(), expected,"")
}
}
@ziogaschr
Copy link

@albertbori I have searched a bit on this topic too and finally I reached to same conclusion as you. :(

@odemolliens
Copy link

} else if propValue is NSNumber {
                propertiesDictionary.setValue((propValue as Float), forKey: propName!)
            }

is missing

@mosspalmer
Copy link

@elecay made a good point regarding datatypes which no-one seems to have addressed. Any non-zero int value is returned as true. As a quick fix for now I've added the NSNumber line by @odemolliens and move bool down in the order of execution so it effectively never gets hit until I can write something to counter the problem.

@cnbuff410
Copy link

In order to compile it from latest XCode, 2 changes are needed:

1 Change "propertiesInAClass" from UnsafePointer to UnsafeMutablePointer
2 Either force unwrap the propName, or use "if let" and wrap line 27 - line 49 in the "if let" block

@shaotianchi
Copy link

that's awesome, I've been using reflection to get the type of value, i did not expect that can use setValue... Thank u

@turowicz
Copy link
Author

I've updated the code with my latest implementation.

@turowicz
Copy link
Author

Tomorrow I will convert it to a github repo so we all can contribute

@LDMFD
Copy link

LDMFD commented Mar 19, 2015

Did you make the github repo? Seems like a good opportunity to merge all the suggestions.

@brduca
Copy link

brduca commented Apr 9, 2015

Awesome job! I'll probably try to do something like that for querystrings. Would it be helpful somehow?

@Pinny3
Copy link

Pinny3 commented Apr 12, 2015

Great work - is there any way to do a deserialization also?

@Nonouf
Copy link

Nonouf commented Apr 14, 2015

@turowicz nice work, it saves me a great time. You just forgot the type Int, Double and Float. Here is the code changed :

public func toDictionary() -> NSDictionary {
        var aClass : AnyClass? = self.dynamicType
        var propertiesCount : CUnsignedInt = 0
        let propertiesInAClass : UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(aClass, &propertiesCount)
        var propertiesDictionary : NSMutableDictionary = NSMutableDictionary()

        for var i = 0; i < Int(propertiesCount); i++ {
            var property = propertiesInAClass[i]
            var propName = NSString(CString: property_getName(property), encoding: NSUTF8StringEncoding)!
            var propType = property_getAttributes(property)
            var propValue : AnyObject! = self.valueForKey(propName);

            if propValue is Serializable {
                propertiesDictionary.setValue((propValue as Serializable).toDictionary(), forKey: propName)
            } else if propValue is Array<Serializable> {
                var subArray = Array<NSDictionary>()
                for item in (propValue as Array<Serializable>) {
                    subArray.append(item.toDictionary())
                }
                propertiesDictionary.setValue(subArray, forKey: propName)
            } else if propValue is Double {
                propertiesDictionary.setValue((propValue as Double), forKey: propName)
            } else if propValue is Int {
                propertiesDictionary.setValue((propValue as Int), forKey: propName)
            } else if propValue is Float {
                propertiesDictionary.setValue((propValue as Float), forKey: propName)
            } else if propValue is NSData {
                propertiesDictionary.setValue((propValue as NSData).base64EncodedStringWithOptions(nil), forKey: propName)
            } else if propValue is Bool {
                propertiesDictionary.setValue((propValue as Bool).boolValue, forKey: propName)
            } else {
                propertiesDictionary.setValue(propValue, forKey: propName)
            }
        }

        // class_copyPropertyList retaints all the
        propertiesInAClass.dealloc(Int(propertiesCount))

        return propertiesDictionary
    }

@turowicz
Copy link
Author

@ALL we should turn it into a pod - I'll make a repo later

@turowicz
Copy link
Author

Moved to https://github.com/Mailcloud/swift-serializer

Please submit your pull request @nono67

@turowicz
Copy link
Author

Issues created for anyone who'd like to contribute https://github.com/Mailcloud/swift-serializer/issues?q=is%3Aopen+is%3Aissue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment