Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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,"")
}
}
@albertbori

This comment has been minimized.

Copy link

commented Jul 25, 2014

Firstly, this is awesome.

Just a couple of things I'd like to point out (modifications):

  1. On line 38, propValue.base64Encoding() seems to be deprecated and caused me compiler errors. Changed it out for: propValue.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
  2. To prevent recursion errors, the following condition was added before line 29:
let kWeakVariableProperty = "W"
var propTypes = NSString(UTF8String:property_getAttributes(property)).componentsSeparatedByString(",") as [String]

if contains(propTypes, kWeakVariableProperty) {
    continue;
}

That way, the same anti-cyclical referencing logic in Swift will prevent cyclical serialization of properties (leading to stack overflow)
3) Had to add NSDate serialization support on line 39:

else if propValue is NSDate
{
    var date = propValue as NSDate
    let dateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "Z"
    var dateString = NSString(format: "/Date(%.0f000%@)/", date.timeIntervalSince1970, dateFormatter.stringFromDate(date))
    propertiesDictionary.setValue(dateString, forKey: propName)
}

All the credit for these ideas go to Porter Hoskins

@MaximShoustin

This comment has been minimized.

Copy link

commented Aug 19, 2014

I get line 20:

UnsafeMutablePointer<objc_property_t>' is not convertible to 'UnsafePointer<objc_property_t>

So I changed to UnsafeMutablePointer

@turowicz

This comment has been minimized.

Copy link
Owner Author

commented Aug 29, 2014

hey guys sorry for not responding - been away for some time, will update the formatter to the most recent version later today - thanks for the input!

@turowicz

This comment has been minimized.

Copy link
Owner Author

commented Aug 29, 2014

updated the code to my latest code base - will look into your points in the afternoon

@elecay

This comment has been minimized.

Copy link

commented Sep 11, 2014

Please be aware of this: http://imgur.com/Ihsrzci

An Int is a Bool and a Bool is a Int according to Swift. The same for Float and Double

@MaximShoustin

This comment has been minimized.

Copy link

commented Sep 16, 2014

Do you need release UnsafePointers? it doesn't have auto-release engine

@ziogaschr

This comment has been minimized.

Copy link

commented Sep 26, 2014

Can you please add support for enumerations?
What are you considering the best way at the moment in Swift for deserializing the JSON back to a new object?

@whitneyland

This comment has been minimized.

Copy link

commented Oct 14, 2014

Any ideas to get optional properties like: var myNumber : Double?

This would allow a value to exist or not exist as happens in json...

@albertbori

This comment has been minimized.

Copy link

commented Oct 15, 2014

For those of you who want a standard ISO8601 date format, change the date transformation lines to use this:

dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
propertiesDictionary.setValue(dateFormatter.stringFromDate(date), forKey: propName)

@ziogaschr, I'm needing the same thing. The problem is that class_copyPropertyList(aClass, &propertyCount) doesn't return enum type properties. Not sure why:

enum SeedType: Int {
    case Seedless = 0
    case Seeds = 1
    case Pit = 2
}

class Fruit : NSObject {
    var name = "Apple"
    var id = 7
    var date = NSDate()
    var seedType = SeedType.Seeds
    var parentFruit:Fruit?
}

var fruit = Fruit()
fruit.parentFruit = Fruit()

var propertyCount : CUnsignedInt = 0
let propertiesInAClass : UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(fruit.dynamicType, &propertyCount)
println("property count: \(propertyCount)")

Results in:

property count: 4

The serialization result being:

{"id":7,"parentFruit":{"name":"Apple","id":7,"date":"2014-10-15T15:59:48.944"},"name":"Apple","date":"2014-10-15T15:59:48.937"}

This means that the enum property is getting skipped for some reason. I'm not sure how to get around that.

_Edit_

The answer is because "class_copyPropertyList only shows properties that are exposed to the Objective-C runtime" Source

So, it looks like this approach will not work for swift enums and optional value-types until Apple changes something.

@ziogaschr

This comment has been minimized.

Copy link

commented Nov 3, 2014

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

@odemolliens

This comment has been minimized.

Copy link

commented Nov 14, 2014

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

is missing

@mosspalmer

This comment has been minimized.

Copy link

commented Dec 2, 2014

@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

This comment has been minimized.

Copy link

commented Dec 26, 2014

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

This comment has been minimized.

Copy link

commented Jan 3, 2015

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

This comment has been minimized.

Copy link
Owner Author

commented Feb 13, 2015

I've updated the code with my latest implementation.

@turowicz

This comment has been minimized.

Copy link
Owner Author

commented Feb 13, 2015

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

@cachvico

This comment has been minimized.

Copy link

commented Mar 19, 2015

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

@brduca

This comment has been minimized.

Copy link

commented Apr 9, 2015

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

@Pinny3

This comment has been minimized.

Copy link

commented Apr 12, 2015

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

@Nonouf

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Owner Author

commented Apr 15, 2015

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

@turowicz

This comment has been minimized.

Copy link
Owner Author

commented Apr 15, 2015

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

Please submit your pull request @nono67

@turowicz

This comment has been minimized.

Copy link
Owner Author

commented Apr 15, 2015

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
You can’t perform that action at this time.