TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | Apache 2 |
ReleasedLast Release | Apr 2018 |
SPMSupports SPM | ✓ |
Maintained by Emilien Stremsdoerfer.
StreemMapper is a simple Swift library to convert JSON to strongly typed
objects.
StreemMapper was originally forked from the awesome mapper library
import StreemMapper
// Conform to the Mappable protocol
struct User: Mappable {
let id: String
let photoURL: NSURL?
// Implement this initializer
init(map: Mapper) throws {
try id = map |> "id"
photoURL = map |> "avatar_url"
}
}
// Create a user!
let json:Any = ...
let user = User.from(JSON:json) // This is a 'User?'
enum UserType: String {
case Normal = "normal"
case Admin = "admin"
}
struct User: Mappable {
let id: String
let type: UserType
init(map: Mapper) throws {
try id = map |> "id"
try type = map |> "user_type"
}
}
Mappable
objects:struct User: Mappable {
let id: String
let name: String
init(map: Mapper) throws {
try id = map |> "id"
try name = map |> "name"
}
}
struct Group: Mappable {
let id: String
let users: [User]
init(map: Mapper) throws {
try id = map |> "id"
users = map |> "users" ?? []
}
}
Convertible
to transparently convert other types from JSON:extension CLLocationCoordinate2D: Convertible {
static func from(value: Any?) throws -> CLLocationCoordinate2D {
guard let location = value as? NSDictionary,
let latitude = location["lat"] as? Double,
let longitude = location["lng"] as? Double else
{
throw MapperError.ConvertibleError(value: value, type: [String: Double].self)
}
return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
}
struct Place: Mappable {
let name: String
let location: CLLocationCoordinate2D
init(map: Mapper) throws {
try name = map |> "name"
try location = map |> "location"
}
}
let JSON = [
"name": "Lyft HQ",
"location": [
"lat": 37.7603392,
"lng": -122.41267249999999,
],
]
let place = Place.from(JSON:json)
private func extractFirstName(object: Any?) throws -> String {
guard let fullName = object as? String else {
throw MapperError.ConvertibleError(value: object, type: String.self)
}
let parts = fullName.characters.split { $0 == " " }.map(String.init)
if let firstName = parts.first {
return firstName
}
throw MapperError.CustomError(field: nil, message: "Couldn't split the string!")
}
struct User: Mappable {
let firstName: String
init(map: Mapper) throws {
try firstName = map.from(field:"name", transformation: extractFirstName)
}
}
struct User: Mappable {
let name: String
let JSON: Any
init(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
struct User: Mappable {
let id: String
let photoURL: 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.
For consistency reasons, an Array is now initialized just like a object.
let users = [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.
struct User: Mappable {
let name: String
init(map: Mapper) throws {
try self.name = map |> "name"
}
}
let JSON = [["name": "John"], ["firstname": "Bob"]]
let users = [User].from(JSON:JSON) // Returns 1 User object with name John
If 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:
struct User: Mappable {
let id: Int
let name: String
init(map: Mapper) throws {
try id = map |> "id"
try name = map |> "name"
}
}
let json = ["data":[["id":1, "name":"Alice"],["id":2, "name":"Bob"],["id":3, "name":"Carol"]]]
let users = [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.
enum DaysOfWeek {
case Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}
struct User: Mappable {
let name: String
let availableDays: [DaysOfWeek]?
init(map: Mapper) throws {
try self.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:
struct User: Mappable {
let date1: Date
let date2: 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:
let json:Any = ...
let user = User.from(JSON:json) // Returns nil if JSON is not dictionary
let users = [User].from(JSON:json) // Returns nil if JSON is not an array
Also, 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
??
operator many times is unsustainable.Self
don’t workStreemMapper is maintained by Emilien Stremsdoerfer and released under
the Apache 2.0 license. See LICENSE for details