Skip to content

Repository files navigation

StreemMapper

StreemMapper is a simple Swift library to convert JSON to strongly typed objects. StreemMapper was originally forked from the awesome mapper library

Installation

use_frameworks!pod"StreemMapper"
github "JustaLab/mapper"

Usage

Simple example:

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?'

Using with enums:

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"}}

Nested Mappable objects:

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"??[]}}

Use Convertible to transparently convert other types from JSON:

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)

Custom Transformations

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)}}

Parse nested or entire objects

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.

Fork modifications

This fork includes few modifications to the original project:

Custom Operator

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"}}

Default Convertibles

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

Arrays

Initialization

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 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.

Root Keys

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")

Array of enums

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

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
}}

Initialization

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 array

Also, because of type inference over the optional type, methods optionalFrom and from have been merged into the single method from.

Open Radars

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 Self don't work

License

StreemMapper is maintained by Emilien Stremsdoerfer and released under the Apache 2.0 license. See LICENSE for details

About

A JSON deserialization library for Swift

Resources

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages