{"id":20456277,"url":"https://github.com/semperos/otto","last_synced_at":"2026-04-16T16:04:02.740Z","repository":{"id":139218210,"uuid":"46388307","full_name":"semperos/otto","owner":"semperos","description":"Declarative model and serialization story for Clojure","archived":false,"fork":false,"pushed_at":"2015-11-18T15:43:54.000Z","size":130,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-15T23:32:03.909Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/semperos.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":"2015-11-18T01:58:25.000Z","updated_at":"2015-11-18T01:59:02.000Z","dependencies_parsed_at":"2023-03-13T18:43:08.504Z","dependency_job_id":null,"html_url":"https://github.com/semperos/otto","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semperos%2Fotto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semperos%2Fotto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semperos%2Fotto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semperos%2Fotto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/semperos","download_url":"https://codeload.github.com/semperos/otto/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242007795,"owners_count":20056790,"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-11-15T11:22:04.009Z","updated_at":"2026-04-16T16:04:02.709Z","avatar_url":"https://github.com/semperos.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Otto: Models and Serialization\n\n\u003cimg src=\"/doc/otto.jpg\" alt=\"Otto\" title=\"Otto\" /\u003e\n\nConstruct models for your domain as Clojure maps, with a declarative data syntax for specifying serialization and deserialization to Clojure maps compatible with other representations.\n\nPrismatic Schema is currently used for the schema validation facilities, whereas simple Clojure functions and multimethods are used to manipulate the domain model map.\n\n[![Build Status](https://travis-ci.org/semperos/otto.svg?branch=master)](https://travis-ci.org/semperos/otto)\n\n## Usage\n\nAn example domain model representing a student:\n\n```clj\n(def Student\n  {:id {:in s/Uuid\n        :out UuidStr}\n   :school-id {:key {:out \"schoolId\"}\n               :in s/Int}\n   :email {:in (s/maybe NonEmptyString)\n           :out (s/maybe s/Str)}\n   :first-name {:key {:out \"firstName\"}\n                :in NonEmptyString\n                :json s/Str}\n   :last-name {:key {:out \"lastName\"}\n               :in (s/maybe NonEmptyString)\n               :out s/Str}\n   :image {:in NonEmptyString}\n   :created-at {:optional? true\n                :json :otto/omit\n                :in (s/maybe Timestamp)}\n   :updated-at {:json :otto/omit\n                :optional? true\n                :in (s/maybe Timestamp)}})\n```\n\nThis represents the \"ideal\" student domain model. Keys are expected to be keywords. Values are maps that may include the following entries:\n\n * `:in`: This is the schema for the value of the domain model entry\n * `:out`: This is the schema for the value of the default serialized representation of this domain model. Users may use arbitrary keywords to specify specific representations, e.g., `:json`.\n * `:key`: This is a map of data about the key itself, which may specify `:optional? true` if the key is optional in the domain model, as well as `:out` or specific representation entries that provide static values for keys (e.g., `:json \"firstName\"` where your key is `:first-name`).\n\n### Schemas ###\n\nOne may extract the schema for the domain model itself using using `domain-schema`:\n\n```clj\n(require '[semperos.otto :as o])\n\n(o/domain-schema Student)\n;=\u003e \u003cPrismatic schema for Student domain model\u003e\n```\n\nOne may extract the schema for one of the supported output formats using `representation-schema`:\n\n```clj\n(require '[semperos.otto :as o])\n\n(o/representation-schema :json Student)\n;=\u003e Prismatic schema for JSON-compatible Clojure map representation of domain model\u003e\n```\n\nFor representations, the `:out` key is used as a fallback when the specified representation is not included in the domain model specification.\n\n### Serialization/Deserialization ###\n\nAll serialization/deserialization in Otto is from **Clojure maps to Clojure maps**. The translation is from Clojure maps that adhere to the schema of the domain model (presumably with all the conveniences of Clojure/JVM data structures) to Clojure maps that adhere to the schema of a particular representation (e.g., a Clojure map destined to be turned into a string of legal JSON) and back again.\n\nThis library provides **no** facilities for generating proper JSON or any other data format. It focuses simply on providing a declarative way to talk about how your domain model is structured and how it needs to be transformed before being rendered into a particular representation.\n\nThe `:in` and `:out` keys dictate what happens on serialization as opposed to deserialization. The `:in` direction is for things that are not in the format of the domain model being deserialized into its format, whereas `:out` or specific representations like `:json` are used to indicate how Clojure domain model values should be transformed on their way out.\n\nFor this purpose, there is a `semperos.otto.coerce/coerce` multimethod that dispatches on a 2-item vector, representing `[from-value to-value]` relationships. By default, Otto ships with some sane defaults for going to and from UUID objects and UUID strings, numbers, etc.\n\nEnd users should **define methods for `coerce`** to support the custom schema types that they use in their domain models.\n\n```clj\n(require '[semperos.otto :as o])\n\n(def student-map {:id #uuid \"43d97569-fdc5-4786-89b6-7d0c8d81e846\"\n                  :school-id 42\n                  :email \"test@test.org\"\n                  :first-name \"Test\"\n                  :last-name \"Tester\"\n                  :image \"test.png\"})\n\n(def student-json-map (o/serialize :json Student student-map))\n\n(= student-map (deserialize Student student-json-map))\n```\n\nBy default, deserialization will look for stringified versions of the Clojure domain model keys if no `:out` or specific representation is provided. If a key should be ommitted, use `:otto/omit` as the value for a representation in the domain model spec.\n\n## License\n\nCopyright © 2015 RentPath, Inc.\n\nDistributed under the Eclipse Public License either version 1.0 or (at\nyour option) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemperos%2Fotto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsemperos%2Fotto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemperos%2Fotto/lists"}