{"id":1731,"url":"https://github.com/ml-archive/Serpent","last_synced_at":"2025-08-02T04:32:32.320Z","repository":{"id":62455193,"uuid":"51374405","full_name":"ml-archive/Serpent","owner":"ml-archive","description":"A protocol to serialize Swift structs and classes for encoding and decoding.","archived":true,"fork":false,"pushed_at":"2022-03-10T09:17:42.000Z","size":3317,"stargazers_count":286,"open_issues_count":11,"forks_count":13,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-10-29T19:02:25.443Z","etag":null,"topics":["alamofire","carthage","cocoapods","decoding","encoding","json","mapper","model-boiler","parsing","swift"],"latest_commit_sha":null,"homepage":"https://nodes-ios.github.io/Serpent/","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/ml-archive.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}},"created_at":"2016-02-09T15:10:04.000Z","updated_at":"2024-05-31T14:48:39.000Z","dependencies_parsed_at":"2022-11-02T00:01:25.774Z","dependency_job_id":null,"html_url":"https://github.com/ml-archive/Serpent","commit_stats":null,"previous_names":["nodes-ios/serpent"],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ml-archive%2FSerpent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ml-archive%2FSerpent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ml-archive%2FSerpent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ml-archive%2FSerpent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ml-archive","download_url":"https://codeload.github.com/ml-archive/Serpent/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228439110,"owners_count":17920018,"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":["alamofire","carthage","cocoapods","decoding","encoding","json","mapper","model-boiler","parsing","swift"],"created_at":"2024-01-05T20:15:54.463Z","updated_at":"2024-12-06T08:31:33.393Z","avatar_url":"https://github.com/ml-archive.png","language":"Swift","funding_links":[],"categories":["Parsing"],"sub_categories":["JSON","Other free courses"],"readme":"### This library has been deprecated and the repo has been archived. \n### The code is still here and you can still clone it, however the library will not receive any more updates or support.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Serpent_icon.png?raw=true\" alt=\"Serpent\"/\u003e\u003c/p\u003e\n\n[![CircleCI](https://circleci.com/gh/nodes-ios/Serpent.svg?style=shield)](https://circleci.com/gh/nodes-ios/Serpent)\n[![Codecov](https://img.shields.io/codecov/c/github/nodes-ios/Serpent.svg)](https://codecov.io/github/nodes-ios/Serpent)\n[![codebeat badge](https://codebeat.co/badges/bf41edec-511c-405d-9036-a7253492c118)](https://codebeat.co/projects/github-com-nodes-ios-serpent)\n[![Carthage Compatible](https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![CocoaPods](https://img.shields.io/cocoapods/v/Serpent.svg)](https://cocoapods.org/pods/Serpent)    \n![Plaforms](https://img.shields.io/badge/platforms-iOS%20|%20macOS%20|%20tvOS%20|%20watchOS%20-lightgrey.svg)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/nodes-ios/Serpent/blob/master/LICENSE)\n\n**Serpent** *(previously known as Serializable)* is a framework made for creating model objects or structs that can be easily serialized and deserialized from/to JSON. It's easily expandable and handles all common data types used when consuming a REST API, as well as recursive parsing of custom objects. Designed for use with Alamofire.\n\nIt's designed to be used together with our helper app, the [![ModelBoiler](http://i.imgur.com/V5UzMVk.png)](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler), making model creation a breeze.\n\nSerpent is implemented using protocol extensions and static typing.\n\n## 📑 Table of Contents\n\n- [🐍 Why Serpent?](#-why-serpent)\n- [📝 Requirements](#-requirements)\n- [📦 Installation](#-installation)\n\t- [Carthage](#carthage)\n\t- [CocoaPods](#cocoapods)\n\t- [Swift Package Manager](#swift-package-manager)\n- [🔧 Setup](#-setup)\n- [💻 Usage](#-usage)\n\t- [Getting started](#getting-started)\n\t- [Using Serpent models](#using-serpent-models)\n\t- [More complex examples](#more-complex-examples)\n\t- [Using with Alamofire](#using-with-alamofire)\n\t- [Date parsing](#date-parsing)\n- [👥 Credits](#-credits)\n- [📄 License](#-license)\n\n## 🐍 Why Serpent?\nThere are plenty of other Encoding and Decoding frameworks available. Why should you use Serpent?\n\n* [Performance](https://github.com/nodes-ios/SerpentPerformanceComparison). Serpent is fast, up to 4x faster than similar frameworks.\n* [Features](https://github.com/nodes-ios/SerpentPerformanceComparison#-feature-comparison). Serpent can parse anything you throw at it. Nested objects, Enums, URLs, UIColor, you name it!\n* [![ModelBoiler](http://i.imgur.com/V5UzMVk.png)](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler). Every framework of this kind requires tedious boilerplate code that takes forever to write.  [![ModelBoiler](http://i.imgur.com/V5UzMVk.png)](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler) generates it for you instantly.\n* [Alamofire Integration](). Using the included Alamofire extensions makes implementing an API call returning parsed model data as simple as doing a one-liner!\n* [Expandability](). Parsing into other datatypes can easily be added.\n* [Persisting](). Combined with our caching framework [Cashier](https://github.com/nodes-ios/Cashier), Serpent objects can be very easily persisted to disk.\n* \u003ca href = \"https://github.com/nodes-ios/SerpentXcodeFileTemplate\"\u003e\u003cimg src = \"https://raw.githubusercontent.com/nodes-ios/SerpentXcodeFileTemplate/master/Serpent/Serpent%20Model.xctemplate/TemplateIcon.png\" height = 25\u003e Serpent Xcode File Template \u003c/a\u003e makes it easier to create the model files in Xcode.\n\n## 📝 Requirements\n\n* iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+\n* Swift 3.0+  \n*(Swift 2.2 \u0026 Swift 2.3 supported in older versions)*\n\n## 📦 Installation\n\n### Carthage\n~~~bash\ngithub \"nodes-ios/Serpent\" ~\u003e 1.0\n~~~\n\n\u003e Last versions compatible with lower Swift versions:  \n\u003e\n\u003e **Swift 2.3**  \n\u003e `github \"nodes-ios/Serpent\" == 0.13.2`\n\u003e\n\u003e **Swift 2.2**  \n\u003e `github \"nodes-ios/Serpent\" == 0.11.2`\n\u003e\n\u003e **NOTE:** Serpent was previously known as **Serializable**.\n\n### CocoaPods\n\nChoose one of the following, add it to your `Podfile` and run `pod install`:\n\n~~~ruby\npod 'Serpent', '~\u003e 1.0' # Just core\npod 'Serpent/Extensions', '~\u003e 1.0' # Includes core and all extensions\npod 'Serpent/AlamofireExtension', '~\u003e 1.0' # Includes core and Alamofire extension\npod 'Serpent/CashierExtension', '~\u003e 1.0' # Includes core and Cashier extension\n~~~\n\n\u003e **NOTE:** CocoaPods only supports Serpent using Swift version 3.0 and higher.\n\n### Swift Package Manager\n\nTo use Serpent as a [Swift Package Manager](https://swift.org/package-manager/) package just add the following to your `Package.swift` file.  \n\n~~~swift\nimport PackageDescription\n\nlet package = Package(\n    name: \"YourPackage\",\n    dependencies: [\n        .Package(url: \"https://github.com/nodes-ios/Serpent.git\", majorVersion: 1)\n    ]\n)\n~~~\n\n\n## 🔧 Setup\n\nWe **highly** recommend you use our [![ModelBoiler](http://i.imgur.com/V5UzMVk.png)](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler) to assist with generating the code needed to conform to Serpent. Instructions for installation and usage can be found at the [Model Boiler GitHub repository](https://github.com/nodes-ios/ModelBoiler).\n\n## 💻 Usage\n\n### Getting started\n\nSerpent supports all primitive types, `enum`, `URL`, `Date`, `UIColor`, other `Serpent` model, and `Array` of all of the aforementioned types. Your variable declarations can have a default value or be optional.\n\nPrimitive types do not need to have an explicit type, if Swift is able to infer it normally. `var name: String = \"\"` works just as well as `var name = \"\"`. Optionals will of course need an explicit type.\n\n\u003e **NOTE:** Enums you create must conform to `RawRepresentable`, meaning they must have an explicit type. Otherwise, the parser won't know what to do with the incoming data it receives.\n\n\n#### Create your model struct or class:\n\n~~~swift\nstruct Foo {\n\tvar id = 0\n\tvar name = \"\"\n\tvar address: String?\n}\n~~~\n\n\u003e **NOTE:** Classes must be marked `final`.\n\n#### Add the required methods for `Encodable` and `Decodable`:\n\n~~~swift\nextension Foo: Serializable {\n    init(dictionary: NSDictionary?) {\n        id      \u003c== (self, dictionary, \"id\")\n        name    \u003c== (self, dictionary, \"name\")\n        address \u003c== (self, dictionary, \"address\")\n    }\n\n    func encodableRepresentation() -\u003e NSCoding {\n        let dict = NSMutableDictionary()\n        (dict, \"id\")      \u003c== id\n        (dict, \"name\")    \u003c== name\n        (dict, \"address\") \u003c== address\n        return dict\n    }\n}\n~~~\n\n\u003e **NOTE:** You can add conformance to `Serializable` which is a type alias for both `Encodable` and `Decodable`.\n\nAnd thats it! If you're using the [![ModelBoiler](http://i.imgur.com/V5UzMVk.png)](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler), this extension will be generated for you, so that you don't need to type it all out for every model you have.\n\n### Using Serpent models\n\nNew instances of your model can be created with a dictionary, e.g. from parsed JSON.\n\n~~~swift\nlet dictionary = try JSONSerialization.jsonObject(with: someData, options: .allowFragments) as? NSDictionary\nlet newModel = Foo(dictionary: dictionary)\n~~~\n\nYou can generate a dictionary version of your model by calling `encodableRepresentation()`:\n\n~~~swift\nlet encodedDictionary = newModel.encodableRepresentation()\n~~~\n\n### More complex examples\n\nIn this example, we have two models, Student and School.\n\n~~~swift\nstruct Student {\n\tenum Gender: String {\n\t\tcase male = \"male\"\n\t\tcase female = \"female\"\n\t\tcase unspecified = \"unspecified\"\n\t}\n\n\tvar name = \"\"\n\tvar age: Int = 0\n\tvar gender: Gender?\n}\n\nstruct School {\n\tenum Sport: Int {\n\t\tcase football\n\t\tcase basketball\n\t\tcase tennis\n\t\tcase swimming\n\t}\n\n\tvar name = \"\"\n\tvar location = \"\"\n\tvar website: URL?\n\tvar students: [Student] = []\n\tvar sports: [Sport]?\n}\n~~~\n\n\nYou can get as complicated as you like, and the syntax will always remain the same. The extensions will be:\n\n~~~swift\nextension Student: Serializable {\n\tinit(dictionary: NSDictionary?) {\n\t\tname   \u003c== (self, dictionary, \"name\")\n\t\tage    \u003c== (self, dictionary, \"age\")\n\t\tgender \u003c== (self, dictionary, \"gender\")\n\t}\n\n\tfunc encodableRepresentation() -\u003e NSCoding {\n\t\tlet dict = NSMutableDictionary()\n\t\t(dict, \"name\")   \u003c== name\n\t\t(dict, \"age\")    \u003c== age\n\t\t(dict, \"gender\") \u003c== gender\n\t\treturn dict\n\t}\n}\n\nextension School: Serializable {\n\tinit(dictionary: NSDictionary?) {\n\t\tname     \u003c== (self, dictionary, \"name\")\n\t\tlocation \u003c== (self, dictionary, \"location\")\n\t\twebsite  \u003c== (self, dictionary, \"website\")\n\t\tstudents \u003c== (self, dictionary, \"students\")\n\t\tsports   \u003c== (self, dictionary, \"sports\")\n\t}\n\n\tfunc encodableRepresentation() -\u003e NSCoding {\n\t\tlet dict = NSMutableDictionary()\n\t\t(dict, \"name\")     \u003c== name\n\t\t(dict, \"location\") \u003c== location\n\t\t(dict, \"website\")  \u003c== website\n\t\t(dict, \"students\") \u003c== students\n\t\t(dict, \"sports\")   \u003c== sports\n\t\treturn dict\n\t}\n}\n~~~\nAgain, the [![ModelBoiler](http://i.imgur.com/V5UzMVk.png)](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler) generates all of this code for you in less than a second!\n\n### Using with Alamofire\n\nSerpent comes integrated with Alamofire out of the box, through an extension on Alamofire's `Request` construct, that adds the function `responseSerializable(completion:unwrapper)`\n\nThe extension uses Alamofire's familiar `Response` type to hold the returned data, and uses its generic associated type to automatically parse the data.\n\nConsider an endpoint returning a single `school` structure matching the struct from the example above. To implement the call, simply add a function to your shared connection manager or where ever you like to put it:\n\n~~~swift\nfunc requestSchool(completion: @escaping (DataResponse\u003cSchool\u003e) -\u003e Void) {\n\trequest(\"http://somewhere.com/school/1\", method: .get).responseSerializable(completion)\n}\n~~~\n\nIn the consuming method you use it like this:\n\n~~~swift\nrequestSchool() { (response) in\n\tswitch response.result {\n\t\tcase .success(let school):\n\t\t\t//Use your new school object!\n\n\t\tcase .failure(let error):\n\t\t\t//Handle the error object, or check your Response for more detail\n\t}\n}\n~~~\n\nFor an array of objects, use the same technique:\n\n~~~swift\nstatic func requestStudents(completion: @escaping (DataResponse\u003c[Student]\u003e) -\u003e Void) {\n\trequest(\"http://somewhere.com/school/1/students\", method: .get).responseSerializable(completion)\n}\n~~~\n\nSome APIs wrap their data in containers. Use the `unwrapper` closure for that. Let's say your `/students` endpoint returns the data wrapped in a `students` object:\n\n~~~json\n{\n\t\"students\" : [\n\t\t{\n\t\t    \"...\" : \"...\"\n\t\t},\n\t\t{\n\t\t    \"...\" : \"...\"\n\t\t}\n\t]\n}\n~~~\n\nThe `unwrapper` closure has 2 input arguments: The `sourceDictionary` (the JSON Response Dictionary) and the `expectedType` (the *type* of the target Serpent). Return the object that will serve as the input for the Serializable initializer.\n\n~~~swift\nstatic func requestStudents(completion: (DataResponse\u003c[Student]\u003e) -\u003e Void) {\n\trequest(\"http://somewhere.com/school/1/students\", method: .get).responseSerializable(completion, unwrapper: { $0.0[\"students\"] })\n}\n~~~\n\nIf you need to unwrap the response data in every call, you can install a default unwrapper using\n\n~~~swift\nParser.defaultWrapper = { sourceDictionary, expectedType in \n\t// You custom unwrapper here... \n\treturn sourceDictionary\n}\n~~~\n\nThe `expectedType` can be used to dynamically determine the key based on the type name using reflection. This is especially useful when handling paginated data.\n\nSee [here](https://github.com/nodes-ios/Nodes) for an example on how we use this in our projects at Nodes.\n\n***NOTE:*** `responseSerializable` Internally calls `validate().responseJSON()` on the request, so you don't have to do that.\n\n### Date parsing\nSerpent can create `Date` objects from the date strings in the JSON. By default, Serpent can parse the date strings from the following formats: `yyyy-MM-dd'T'HH:mm:ssZZZZZ`, `yyyy-MM-dd'T'HH:mm:ss`, `yyyy-MM-dd`. If you need to parse other date formats, you can do it by adding this line to your code (for example, in `AppDelegate`'s `didFinishLaunchingWithOptions:`:\n\n~~~swift\nDate.customDateFormats = [\"yyyyMMddHHmm\", \"yyyyMMdd\"]    // add the custom date formats you need here\n~~~\n\nThe custom date formats won't replace the default ones, they will be still supported. \n\n## 👥 Credits\nMade with ❤️ at [Nodes](http://nodesagency.com).\n\n## 📄 License\n**Serpent** is available under the MIT license. See the [LICENSE](https://github.com/nodes-ios/Serpent/blob/master/LICENSE) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fml-archive%2FSerpent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fml-archive%2FSerpent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fml-archive%2FSerpent/lists"}