StreemMapper is a simple Swift library to convert JSON to strongly typed objects. StreemMapper was originally forked from the awesome mapper library
With CocoaPods
use_frameworks!pod"StreemMapper"With Carthage
github "JustaLab/mapper"
import StreemMapper
// Conform to the Mappable protocol
structUser:Mappable{letid:StringletphotoURL:NSURL?
// Implement this initializer
init(map:Mapper)throws{try id = map |>"id"
photoURL = map |>"avatar_url"}}
// Create a user!
letjson:Any=...letuser=User.from(JSON:json) // This is a 'User?'enumUserType:String{case Normal ="normal"case Admin ="admin"}structUser:Mappable{letid:Stringlettype:UserTypeinit(map:Mapper)throws{try id = map |>"id"try type = map |>"user_type"}}structUser:Mappable{letid:Stringletname:Stringinit(map:Mapper)throws{try id = map |>"id"try name = map |>"name"}}structGroup:Mappable{letid:Stringletusers:[User]init(map:Mapper)throws{try id = map |>"id"
users = map |>"users"??[]}}extensionCLLocationCoordinate2D:Convertible{staticfunc from(value:Any?)throws->CLLocationCoordinate2D{guardlet location = value as?NSDictionary,let latitude =location["lat"]as?Double,let longitude =location["lng"]as?Doubleelse{throwMapperError.ConvertibleError(value: value, type:[String:Double].self)}returnCLLocationCoordinate2D(latitude: latitude, longitude: longitude)}}structPlace:Mappable{letname:Stringletlocation:CLLocationCoordinate2Dinit(map:Mapper)throws{try name = map |>"name"try location = map |>"location"}}letJSON=["name":"Lyft HQ","location":["lat":37.7603392,"lng":-122.41267249999999,],]letplace=Place.from(JSON:json)privatefunc extractFirstName(object:Any?)throws->String{guardlet fullName = object as?Stringelse{throwMapperError.ConvertibleError(value: object, type:String.self)}letparts= fullName.characters.split{ $0 ==""}.map(String.init)iflet firstName = parts.first {return firstName
}throwMapperError.CustomError(field:nil, message:"Couldn't split the string!")}structUser:Mappable{letfirstName:Stringinit(map:Mapper)throws{try firstName = map.from(field:"name", transformation: extractFirstName)}}structUser:Mappable{letname:StringletJSON:Anyinit(map:Mapper)throws{
// Access the 'first' key nested in a 'name' dictionary
try name = map |>"name.first"
// Access the original JSON (maybe for use with a transformation)
try JSON = map |>""}}See the docstrings and tests for more information and examples.
This fork includes few modifications to the original project:
This fork includes a custom operator that allows you to parse the Mapper object super cleanly:
import Mapper
// Conform to the Mappable protocol
structUser:Mappable{letid:StringletphotoURL:NSURL?
// Implement this initializer
init(map:Mapper)throws{try id = map |>"id"
photoURL = map |>"avatar_url"}}Many convertibles are now supported in this fork, here is the list of all supported types, in bold are thoses added.
- String
- Int
- UInt
- Int8
- UInt8
- Int16
- UInt16
- Int32
- UInt32
- Int64
- UInt64
- Float
- Double
- Bool
- NSNumber
- NSDictionary
- NSArray
- NSURL
- NSDate from a timestamp
For consistency reasons, an Array is now initialized just like a object.
letusers=[User].from(JSON:json) // Returns a [User]?Also when parsing an array, the original library doesn't allow you to have a malformed object within the JSON array. It will just return nil. This fork allows the creation of an array and will just omit the malformed objects.
structUser:Mappable{letname:Stringinit(map:Mapper)throws{tryself.name = map |>"name"}}letJSON=[["name":"John"],["firstname":"Bob"]]letusers=[User].from(JSON:JSON) // Returns 1 User object with name JohnIf no object can be parsed within the JSON array, this will return an empty array. If the JSON is not array, it will return nil.
With StreemMapper, you can now also parse an array with a given root key. For example if you have a JSON like this:
{
"data" : [
{"id":1, "name":"Alice"},
{"id":2, "name":"Bob"},
{"id":3, "name":"Carol"}
]
}You can parse the array directly, without having to add a wrapper object:
structUser:Mappable{letid:Intletname:Stringinit(map:Mapper)throws{try id = map |>"id"try name = map |>"name"}}letjson=["data":[["id":1,"name":"Alice"],["id":2,"name":"Bob"],["id":3,"name":"Carol"]]]letusers=[User].from(JSON:json, rootKey:"data")Finally, this fork allows you to parse an optional array of enums, this is especially useful, if you're unsure about the JSON you receive and prefer to return nil or an empty array instead of throwing an exception.
enumDaysOfWeek{case Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}structUser:Mappable{letname:StringletavailableDays:[DaysOfWeek]?init(map:Mapper)throws{tryself.name = map |>"name"
availableDays = map |>"available_days" //returns nil if key is not there
}}Dates can now be parsed using a formatter or a timestamp:
structUser:Mappable{letdate1:Dateletdate2:Date?init(map:Mapper)throws{try date1 = map |>("date1","MMMM dd, yyyy h:mm:ss a zzz") // will try to parse as a string with the given format
date2 = map |>"date2" //will try to parse it as a timestamp
}}When creating an object there is no need any more to parse the initial object into a Dictionary or Array. The library takes care of it and will return nil if an array is passed instead of a dictionary and vice versa.
For instance:
letjson:Any=...letuser=User.from(JSON:json) // Returns nil if JSON is not dictionary
letusers=[User].from(JSON:json) // Returns nil if JSON is not an arrayAlso, because of type inference over the optional type, methods optionalFrom and from have been merged into the single method from.
These radars have affected the current implementation of Mapper
- rdar://23376350 Protocol extensions with initializers do not work in extensions
- rdar://23358609 Protocol extensions with initializers do not play well with classes
- rdar://23226135 Can't conform to protocols with similar generic function signatures
- rdar://23147654 Generic functions are not differentiated by their ability to throw
- rdar://23695200
Using the
??operator many times is unsustainable. - rdar://23697280 Lazy collection elements can be evaluated multiple times.
- rdar://23718307
Non final class with protocol extensions returning
Selfdon't work
StreemMapper is maintained by Emilien Stremsdoerfer and released under the Apache 2.0 license. See LICENSE for details