{"id":13465521,"url":"https://github.com/Anviking/Decodable","last_synced_at":"2025-03-25T16:32:09.596Z","repository":{"id":34777608,"uuid":"38760458","full_name":"Anviking/Decodable","owner":"Anviking","description":"[Probably deprecated] Swift 2/3 JSON unmarshalling done (more) right","archived":false,"fork":false,"pushed_at":"2023-10-23T07:18:46.000Z","size":663,"stargazers_count":1040,"open_issues_count":35,"forks_count":73,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-10-15T02:41:21.112Z","etag":null,"topics":["json","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"Seraphaestus/HistoricizedMedicine","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Anviking.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-07-08T14:48:44.000Z","updated_at":"2024-10-12T12:29:31.000Z","dependencies_parsed_at":"2024-01-02T23:55:07.450Z","dependency_job_id":null,"html_url":"https://github.com/Anviking/Decodable","commit_stats":{"total_commits":317,"total_committers":21,"mean_commits":"15.095238095238095","dds":"0.19242902208201895","last_synced_commit":"ab84baf213f326a95ef16be6ea74430c56d3b542"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anviking%2FDecodable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anviking%2FDecodable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anviking%2FDecodable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anviking%2FDecodable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Anviking","download_url":"https://codeload.github.com/Anviking/Decodable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222074991,"owners_count":16926641,"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":["json","swift"],"created_at":"2024-07-31T15:00:31.521Z","updated_at":"2024-10-29T17:31:07.276Z","avatar_url":"https://github.com/Anviking.png","language":"Swift","funding_links":[],"categories":["Libs","Data Management [🔝](#readme)","Swift","Data and Storage","Parsing"],"sub_categories":["Data Management"],"readme":"# Decodable\nSimple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by [Argo](http://github.com/thoughtbot/Argo), but without a bizillion functional operators.\n\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Cocoapods version](https://cocoapod-badges.herokuapp.com/v/Decodable/badge.png)](https://cocoapods.org/pods/Decodable)\n[![Platforms](https://cocoapod-badges.herokuapp.com/p/Decodable/badge.png)](https://cocoadocs.org/docsets/NSStringMask)\n[![Travis](https://img.shields.io/travis/Anviking/Decodable/master.svg)](https://travis-ci.org/Anviking/Decodable/branches)\n\n\n```swift\n\nstruct Repository {\n    let name: String\n    let description: String\n    let stargazersCount: Int\n    let language: String?\n    let sometimesMissingKey: String?\n    \n    let owner: User // Struct conforming to Decodable\n    let defaultBranch: Branch // Struct NOT conforming to Decodable\n    \n    var fullName: String { return \"\\(owner.login)/\\(name)\" }\n}\n\nextension Repository: Decodable {\n    static func decode(j: Any) throws -\u003e Repository {\n        return try Repository(\n                    name: j =\u003e \"nested\" =\u003e \"name\", \n                    description: j =\u003e \"description\", \n                    stargazersCount: j =\u003e \"stargazers_count\", \n                    language: j =\u003e \"language\", \n                    sometimesMissingKey: j =\u003e? \"sometimesMissingKey\",\n                    owner: j =\u003e \"owner\", \n                    defaultBranch: Branch(name: j =\u003e \"default_branch\")\n                )\n    }\n}\n\ndo {\n    let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])\n    let repo = try [Repository].decode(json)\n} catch {\n    print(error)\n}\n```\n\n## How does it work?\n\n### A protocol\n```swift\npublic protocol Decodable {\n    static func decode(json: Any) throws -\u003e Self\n}\n```\n### A parse-function\n```swift\npublic func parse\u003cT\u003e(json: Any, path: [String], decode: (Any throws -\u003e T)) throws -\u003e T\n```\n\n### And shameless operator-overloading\nThe too-many generated overloads, all calling the `parse`-function, can be found in [Overloads.swift](https://github.com/Anviking/Decodable/blob/master/Sources/Overloads.swift). Return types include `T?`, `[T?]`, `[T?]?`, `Any` and `[String: T]?`. When conditional protocol conformance is supported in Swift this won't be necessary, and automagic decoding of infinitly nested generic types (like `[[[[[[[[[A???]]: B]]]?]]?]]`) would work.\n\nAn overload may look like this:\n```swift\npublic func =\u003e \u003cT: Decodable\u003e(json: Any, keyPath: KeyPath) throws -\u003e T\n```\n\n## KeyPaths\nKeypaths can be created from string and array literals as well as with explicit initializers. They can also be joined using the operators `=\u003e` and `=\u003e?`. `=\u003e?` is another operator that indicates that `nil` should be returned if the key to the right is missing.\n\n- When composing `=\u003e` and `=\u003e?` operators in the same keypath, the strictness of `=\u003e` is still honoured.\n- Optional key paths (`=\u003e?`) require an optional return type\n\n```swift\nlet a: KeyPath = \"a\"\nlet b: KeyPath = [\"a\", \"b\"]\nlet c: KeyPath = \"a\" =\u003e \"b\" =\u003e \"c\"\nlet string: String? = json =\u003e? \"key1\" =\u003e \"key2\" =\u003e \"key3\"`\n                                ^^^^ allowed to be missing\n```\n## Errors\nErrors will be caught and rethrown in the decoding process to backpropagate metadata, like the JSON object that failed decoding, the key path to it, and the root JSON object.\n\nFrom [DecodingError.swift](https://github.com/anviking/decodable/tree/master/Sources/DecodingError.swift):\n```swift\npublic enum DecodingError: ErrorProtocol, Equatable {\n    /// Thrown when optional casting from `Any` fails.\n    ///\n    /// This can happen both when trying to access a key on a object\n    /// that isn't a `NSDictionary`, and failing to cast a `Castable`\n    /// primitive.\n    case typeMismatch(expected: Any.Type, actual: Any.Type, Metadata)\n    \n    /// Thrown when a given, required, key was not found in a dictionary.\n    case missingKey(String, Metadata)\n    \n    /// Thrown from the `RawRepresentable` extension when\n    /// `init(rawValue:)` returned `nil`.\n    case rawRepresentableInitializationError(rawValue: Any, Metadata)\n    \n    /// When an error is thrown that isn't `DecodingError`, it \n    /// will be wrapped in `DecodingError.other` in order to also provide\n    /// metadata about where the error was thrown.\n    case other(ErrorProtocol, Metadata)\n}\n```\n\n```swift\nlet dict: NSDictionary = [\"object\": [\"repo\": [\"owner\": [\"id\" : 1, \"login\": \"anviking\"]]]]\n\ndo {\n    let username: String = try dict =\u003e \"object\" =\u003e \"repo\" =\u003e \"owner\" =\u003e \"name\"\n} catch let error {\n    print(error)\n}\n//\n// MissingKeyError at object.repo.owner: name in {\n//    id = 1;\n//    login = anviking;\n// }\n```\n\n## Handling Errors\nExpressions like `j =\u003e \"key\"` will throw directly, and `catch`-statements can be used to create the most complex error handling behaviours. This also means that `try?` can be used to return nil if *anything* goes wrong instead of throwing.\n\nFor convenience there is an operator, `=\u003e?`, that only returns nil on missing keys, for APIs that indicate `null` in that manner, and to aid working with different response formats.\n\n| Overload | Null Behaviour | Missing Key Behavior  |Type Mismatch Behaviour | Errors in subobjects | \n| ------------- |:-------------:|:-----:|:-----:|:-----:|\n|  `=\u003e -\u003e T`| throws | throws | throws | uncaught (throws) | \n|  `=\u003e -\u003e T?`| nil | throws | throws | uncaught (throws) | \n|  `=\u003e? -\u003e T?`| nil | nil | throws | uncaught (throws) | \n|  `try? =\u003e -\u003e T `| nil | nil | nil | caught (nil) | \n\n## Customization\n`Int`, `Double`,`String`, `Bool`, `Date` (ISO8601), `NSArray`, and `NSDictionary` types that conform to `DynamicDecodable` with the following declaration:\n```swift\npublic protocol DynamicDecodable {\n    associatedtype DecodedType\n    static var decoder: (Any) throws -\u003e DecodedType {get set}\n}\n```\nThis allows Decodable to implement default decoding closures while allowing you to override them as needed.\n```swift\n// Lets extend Bool.decoder so that it accepts certain strings:\nBool.decoder = { json in\n    switch json {\n    case let str as String where str == \"true\":\n        return true\n    case let str as String where str == \"false\":\n        return false\n    default:\n        return try cast(json)\n    }\n}\n```\n\nNote that when extending new types to conform to `Decodable` there is really no point in conforming to `DynamicDecodable` since you already control the implementation. Also note that the `decoder` properties are intended as \"set once\". If you need different behaviour on different occations, please create custom decode functions.\n\nThe default `Date.decoder` uses a ISO8601 date formatter. If you don't want to create your own decode closure there's a helper:\n```swift\nDate.decoder = Date.decoder(using: formatter)\n```\n\n## When `Decodable` isn't enough\nDon't be afraid of not conforming to `Decodable`.\n```swift\nlet array = try NSArray.decode(json =\u003e \"list\").map {\n    try Contribution(json: $0, repository: repo)\n}\n```\n\n## Tips\n- You can use `Decodable` with classes. Just make sure to either call a `required` initializer on self (e.g `self.init`) and return `Self`, or make your class `final`. ( [This](http://stackoverflow.com/questions/26495586/best-practice-to-implement-a-failable-initializer-in-swift) might be a problem though)\n- The `Decodable`-protocol and the `=\u003e`-operator should in no way make you committed to use them everywhere.\n\n## Compatibility\n\n| Swift version | Compatible tag or branch |\n| --- | --- |\n| Swift 4.0 | `0.6.0` |\n| Swift 3.0 | `v0.5` |\n| Swift 2.3 | `v0.4.4`|\n| Swift 2.2 | `v0.4.3`|\n\n## Note on Swift 4.0 usage\nDue to collisions with the standard library you will have to import ambiguous symbols specifically, in addition to `Decodable` as a whole.\n\nThis means you likely want the following\n```swift\nimport Decodable\nimport protocol Decodable.Decodable\n```\nand you can import other symbols, e.g `KeyPath`, `DecodingError`, in a simlilar fashion (using `import struct` and `import enum`)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAnviking%2FDecodable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAnviking%2FDecodable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAnviking%2FDecodable/lists"}