{"id":1723,"url":"https://github.com/Nike-Inc/Elevate","last_synced_at":"2025-08-06T13:32:04.437Z","repository":{"id":9423968,"uuid":"57251633","full_name":"Nike-Inc/Elevate","owner":"Nike-Inc","description":"Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable.","archived":false,"fork":false,"pushed_at":"2022-07-22T02:08:21.000Z","size":286,"stargazers_count":612,"open_issues_count":2,"forks_count":41,"subscribers_count":28,"default_branch":"master","last_synced_at":"2024-04-14T09:40:40.280Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nike-Inc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-27T21:57:35.000Z","updated_at":"2024-03-14T11:03:58.000Z","dependencies_parsed_at":"2022-08-07T05:01:00.730Z","dependency_job_id":null,"html_url":"https://github.com/Nike-Inc/Elevate","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nike-Inc%2FElevate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nike-Inc%2FElevate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nike-Inc%2FElevate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nike-Inc%2FElevate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nike-Inc","download_url":"https://codeload.github.com/Nike-Inc/Elevate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228905447,"owners_count":17989768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-01-05T20:15:54.278Z","updated_at":"2024-12-09T14:30:45.065Z","avatar_url":"https://github.com/Nike-Inc.png","language":"Swift","funding_links":[],"categories":["Parsing","Libs","Data and Storage","Data Management [🔝](#readme)"],"sub_categories":["JSON","Data Management","Other free courses"],"readme":"# Elevate\n\n[![Build Status](https://travis-ci.org/Nike-Inc/Elevate.svg?branch=master)](https://travis-ci.org/Nike-Inc/Elevate)\n[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Elevate.svg)](https://img.shields.io/cocoapods/v/Elevate.svg)\n[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Platform](https://img.shields.io/cocoapods/p/Elevate.svg?style=flat)](http://cocoadocs.org/docsets/Elevate)\n\nElevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable.\n\n\u003e Elevate should no longer be used for new feature development.\n\u003e We recommend using the `Codable` protocol provided by Apple in the `Foundation` framework in its place.\n\u003e We will continue to support and update Elevate for the foreseeable future.\n\n## Features\n\n- [X] Validation of full JSON payload\n- [X] Parse complex JSON into strongly typed objects\n- [X] Support for optional and required values\n- [X] Convenient and flexible protocols to define object parsing\n- [X] Large object graphs can be parsed into their component objects\n- [X] Error aggregation across entire object graph\n\n## Requirements\n\n- iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+\n- Xcode 10.2+\n- Swift 5.0+\n\n## Communication\n\n- Need help? Open an issue.\n- Have a feature request? Open an issue.\n- Find a bug? Open an issue.\n- Want to contribute? Fork the repo and submit a pull request.\n\n## Installation\n\n### CocoaPods\n\n[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects.\nYou can install it with the following command:\n\n```bash\n[sudo] gem install cocoapods\n```\n\n\u003e CocoaPods 1.3+ is required.\n\nTo integrate Elevate into your Xcode project using CocoaPods, specify it in your [Podfile](http://guides.cocoapods.org/using/the-podfile.html):\n\n```ruby\nsource 'https://github.com/CocoaPods/Specs.git'\nplatform :ios, '11.0'\nuse_frameworks!\n\npod 'Elevate', '~\u003e 3.0'\n```\n\n### Carthage\n\n[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.\n\nYou can install Carthage with Homebrew using the following command:\n\n```bash\nbrew update\nbrew install carthage\n```\n\nTo integrate Elevate into your Xcode project using Carthage, specify it in your [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile):\n\n```bash\ngithub \"Nike-Inc/Elevate\" ~\u003e 3.0\n```\n\nTo build Elevate on iOS only, use the following Carthage command:\n\n```bash\ncarthage update --platform iOS\n```\n\n---\n\n## Usage\n\nElevate aims to make JSON parsing and validation simple, yet robust.\nThis is achieved through a set of protocols and classes that can be utilized to create `Decodable` and `Decoder` classes.\nBy using Elevate's parsing infrastructure, you'll be able to easily parse JSON data into strongly typed model objects or simple dictionaries by specifying each property key path and its associated type.\nElevate will validate that the keys exist (if they're not optional) and that they are of the correct type.\nValidation errors will be aggregated as the JSON data is parsed.\nIf an error is encountered, a `ParserError` will be thrown.\n\nElevate also supports encoding model objects back into JSON objects through the light-weight `Encodable` protocol.\nConvenience extensions have been added to collection types to make it easy to encode nested objects in a single pass.\n\n### Parsing JSON with Elevate\n\nAfter you have made your model objects `Decodable` or implemented a `Decoder` for them, parsing with Elevate is as simple as:\n\n```swift\nlet avatar: Avatar = try Elevate.decodeObject(from: data, atKeyPath: \"response.avatar\")\n```\n\n\u003e Pass an empty string into `atKeyPath` if your object or array is at the root level. \n\n### Creating Decodables\n\nIn the previous example `Avatar` implements the `Decodable` protocol.\nBy implementing the `Decodable` protocol on an object, it can be used by Elevate to parse avatars from JSON data as a top-level object, a sub-object, or even an array of avatar objects.\n\n```swift\npublic protocol Decodable {\n    init(json: Any) throws\n}\n```\n\nThe `json: Any` will typically be a `[String: Any]` instance that was created from the `JSONSerialization` APIs.\nUse the Elevate `Parser.parseEntity` method to define the structure of the JSON data to be validated and perform the parsing.\n\n```swift\nstruct Person {\n    let identifier: String\n    let name: String\n    let nickname: String?\n    let birthDate: Date\n    let isMember: Bool?\n    let addresses: [Address]\n}\n\nextension Person: Elevate.Decodable {\n    fileprivate struct KeyPath {\n        static let id = \"identifier\"\n        static let name = \"name\"\n        static let nickname = \"nickname\"\n        static let birthDate = \"birthDate\"\n        static let isMember = \"isMember\"\n        static let addresses = \"addresses\"\n    }\n\n    init(json: Any) throws {\n        let dateDecoder = DateDecoder(dateFormatString: \"yyyy-MM-dd\")\n\n        let entity = try Parser.parseEntity(json: json) { schema in\n            schema.addProperty(keyPath: KeyPath.id, type: .int)\n            schema.addProperty(keyPath: KeyPath.name, type: .string)\n            schema.addProperty(keyPath: KeyPath.nickname, type: .string, optional: true)\n            schema.addProperty(keyPath: KeyPath.birthDate, type: .string, decoder: dateDecoder)\n            schema.addProperty(keyPath: KeyPath.isMember, type: .bool, optional: true)\n            schema.addProperty(keyPath: KeyPath.addresses, type: .array, decodableType: Address.self)\n        }\n\n        self.identifier = entity \u003c-! KeyPath.id\n        self.name = entity \u003c-! KeyPath.name\n        self.nickname = entity \u003c-? KeyPath.nickname\n        self.birthDate = entity \u003c-! KeyPath.birthDate\n        self.isMember = entity \u003c-? KeyPath.isMember\n        self.addresses = entity \u003c--! KeyPath.addresses\n    }\n}\n```\n\nImplementing the `Decodable` protocol in this way allows you to create fully intialized structs that can contain non-optional constants from JSON data.\n\nSome other things worth noting in this example:\n\n1. The `Decodable` protocol conformance was implemented as an extension on the struct.\nThis allows the struct to keep its automatic memberwise initializer.\n2. Standard primitive types are supported as well as `URL`, `Array`, and `Dictionary` types.\nSee `ParserPropertyProtocol` definition for the full list.\n3. Elevate facilitates passing a parsed property into a `Decoder` for further manipulation.\nSee the `birthDate` property in the example above.\nThe `DateDecoder` is a standard `Decoder` provided by Elevate to make date parsing hassle free.\n4. A `Decoder` or `Decodable` type can be provided to a property of type `.Array` to parse each item in the array to that type.\nThis also works with the `.Dictionary` type to parse a nested JSON object.\n5. The parser guarantees that properties will be of the specified type.\nTherefore, it is safe to use the custom operators to automatically extract the `Any` value from the `entity` dictionary and cast it to the return type.\n\n### Property Extraction Operators\n\nElevate contains four property extraction operators to make it easy to extract values out of the `entity` dictionary and cast the `Any` value to the appropriate type.\n\n* `\u003c-!` - Extracts the value from the `entity` dictionary for the specified key.\nThis operator should only be used on non-optional properties.\n* `\u003c-?` - Extracts the optional value from the `entity` dictionary for the specified key.\nThis operator should only be used on optional properties.\n* `\u003c--!` - Extracts the array from the `entity` dictionary for the specified key as the specified array type.\nThis operator should only be used on non-optional array properties.\n* `\u003c--?` - Extracts the array from the `entity` dictionary for the specified key as the specified optional array type.\n\n### Creating Encodables\n\nExtending a model object to conform to the `Encodable` protocol is less involved than making it `Decodable`.\nSince your object is already strongly typed, it only needs to be converted into a JSON friendly `Any` object.\nBuilding on the previous `Person` type, let's make it conform to the `Encodable` protocol.\n\n```swift\nextension Person: Elevate.Encodable {\n    var json: Any {\n        var json: [String: Any] = [\n            KeyPath.id: identifier,\n            KeyPath.name: name,\n            KeyPath.birthDate: birthDate,\n            KeyPath.addresses: addresses.json\n        ]\n\n        if let nickname = nickname { json[KeyPath.nickname] = nickname }\n        if let isMember = isMember { json[KeyPath.isMember] = isMember }\n\n        return json\n    }\n}\n```\n\nAs you can see in the example, converting the `Person` into a JSON dictionary is straightforward.\nIt's also easy to convert the array of `Address` objects into JSON by calling the `json` property on the array.\nThis works because `Address` also conforms to `Encodable`.\nThe collection type extensions on `Array`, `Set` and `Dictionary` make it easy to convert a complex objects with multiple layers of `Encodable` objects into a JSON objects.\n\n---\n  \n## Advanced Usage\n\n### Decoders\n\nIn most cases implementing a `Decodable` model object is all that is needed to parse JSON using Elevate.\nThere are some instances though where you will need more flexibility in the way that the JSON is parsed.\nThis is where the `Decoder` protocol comes in.\n\n```swift\npublic protocol Decoder {\n    func decode(_ object: Any) throws -\u003e Any\n}\n```\n\nA `Decoder` is generally implemented as a separate object that returns instances of the desired model object.\nThis is useful when you have multiple JSON mappings for a single model object, or if you are aggregating data across multiple JSON payloads.\nFor example, if there are two separate services that return JSON for `Avatar` objects that have a slightly different property structure, a `Decoder` could be created for each mapping to handle them individually.\n\n\u003e The input type and output types are intentionally vague to allow for flexibility.\nA `Decoder` can return any type you want -- a strongly typed model object, a dictionary, etc.\nIt can even dynamically return different types at runtime if needed.\n\n#### Using Multiple Decoders\n\n```swift\nclass AvatarDecoder: Elevate.Decoder {\n    func decode(_ object: Any) throws -\u003e Any {\n        let urlKeyPath = \"url\"\n        let widthKeyPath = \"width\"\n        let heightKeyPath = \"height\"\n\n        let entity = try Parser.parseEntity(json: object) { schema in\n            schema.addProperty(keyPath: urlKeyPath, type: .url)\n            schema.addProperty(keyPath: widthKeyPath, type: .int)\n            schema.addProperty(keyPath: heightKeyPath, type: .int)\n        }\n\n        return Avatar(\n            URL: entity \u003c-! urlKeyPath,\n            width: entity \u003c-! widthKeyPath,\n            height: entity \u003c-! heightKeyPath\n        )\n    }\n}\n```\n\n```swift\nclass AlternateAvatarDecoder: Elevate.Decoder {\n    func decode(_ object: Any) throws -\u003e Any {\n        let locationKeyPath = \"location\"\n        let wKeyPath = \"w\"\n        let hKeyPath = \"h\"\n\n        let entity = try Parser.parseEntity(json: object) { schema in\n            schema.addProperty(keyPath: locationKeyPath, type: .url)\n            schema.addProperty(keyPath: wKeyPath, type: .int)\n            schema.addProperty(keyPath: hKeyPath, type: .int)\n        }\n\n        return Avatar(\n            URL: entity \u003c-! locationKeyPath,\n            width: entity \u003c-! wKeyPath,\n            height: entity \u003c-! hKeyPath\n        )\n    }\n}\n```\n\nThen to use the two different `Decoder` objects with the `Parser`:\n\n```swift\nlet avatar1: Avatar = try Elevate.decodeObject(\n    from: data1, \n    atKeyPath: \"response.avatar\", \n    with: AvatarDecoder()\n)\n\nlet avatar2: Avatar = try Elevate.decodeObject(\n    from: data2, \n    atKeyPath: \"alternative.response.avatar\", \n    with: AlternateAvatarDecoder()\n)\n```\n\nEach `Decoder` is designed to handle a different JSON structure for creating an `Avatar`.\nEach uses the key paths specific to the JSON data it's dealing with, then maps those back to the properties on the `Avatar` object.\nThis is a very simple example to demonstration purposes.\nThere are MANY more complex examples that could be handled in a similar manner via the `Decoder` protocol.\n\n### Decoders as Property Value Transformers\n\nA second use for the `Decoder` protocol is to allow for the value of a property to be further manipulated.\nThe most common example is a date string.\nHere is how the `DateDecoder` implements the `Decoder` protocol:\n  \n```swift\npublic func decode(_ object: Any) throws -\u003e Any {\n    if let string = object as? String {\n        return try dateFromString(string, withFormatter:self.dateFormatter)\n    } else {\n        let description = \"DateParser object to parse was not a String.\"\n        throw ParserError.Validation(failureReason: description)\n    }\n}\n```\n  \nAnd here is how it's used to parse a JSON date string:\n  \n```swift\nlet dateDecoder = DateDecoder(dateFormatString: \"yyyy-MM-dd 'at' HH:mm\")\n\nlet entity = try Parser.parseEntity(data: data) { schema in\n    schema.addProperty(keyPath: \"dateString\", type: .string, decoder: dateDecoder)\n}\n```\n\nYou are free to create any decoders that you like and use them with your properties during parsing.\nSome other uses would be to create a `StringToBoolDecoder` or `StringToFloatDecoder` that parses a `Bool` or `Float` from a JSON string value.\nThe `DateDecoder` and `StringToIntDecoder` are already included in Elevate for your convenience.\n  \n---\n  \n## Creators\n\n* [Eric Appel](https://github.com/EricAppel) - [@EricAppel](http://twitter.com/EricAppel)\n* [Christian Noon](https://github.com/cnoon) - [@Christian_Noon](http://twitter.com/Christian_Noon)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNike-Inc%2FElevate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNike-Inc%2FElevate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNike-Inc%2FElevate/lists"}