{"id":13801546,"url":"https://github.com/fomkin/pushka","last_synced_at":"2025-05-13T11:31:23.930Z","repository":{"id":36710522,"uuid":"41017075","full_name":"fomkin/pushka","owner":"fomkin","description":"ABANDONED Pure Scala serialization library with annotations","archived":true,"fork":false,"pushed_at":"2019-06-12T09:24:40.000Z","size":110,"stargazers_count":74,"open_issues_count":9,"forks_count":10,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-18T16:57:35.711Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fomkin.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-08-19T06:24:31.000Z","updated_at":"2022-09-23T09:03:18.000Z","dependencies_parsed_at":"2022-09-26T21:51:07.241Z","dependency_job_id":null,"html_url":"https://github.com/fomkin/pushka","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomkin%2Fpushka","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomkin%2Fpushka/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomkin%2Fpushka/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomkin%2Fpushka/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fomkin","download_url":"https://codeload.github.com/fomkin/pushka/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253932883,"owners_count":21986469,"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-08-04T00:01:24.164Z","updated_at":"2025-05-13T11:31:23.489Z","avatar_url":"https://github.com/fomkin.png","language":"Scala","readme":"# Pushka\n\n[![Build Status](https://travis-ci.org/fomkin/pushka.svg?branch=develop)](https://travis-ci.org/fomkin/pushka) [![Join the chat at https://gitter.im/fomkin/pushka](https://badges.gitter.im/fomkin/pushka.svg)](https://gitter.im/fomkin/pushka?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\nPushka is a serialization library implemented without any runtime reflection. It created to reach well human readability of output JSON and good performance. Pushka works well both on Scala (2.10, 2.11, 2.12) and Scala.js.\n\n# Motivation\n\n1. The most of serialization libraries write case classes \"as is\". For example, if we have `Option` value it will be written with some kind of wrapper. In the case of sealed traits, most libraries write metadata: trait name and case class name. This makes JSON unreadable by human and makes it useless for creating public API. We want to achieve high human readability of output JSON: no wrappers if possible, no metadata ever.\n\n2. Codebase simplicity. In our work, we encountered that some libraries based on implicit macroses (including shapeless based) fails on our data. In this project, we want to make the code as simple as possible to find bugs more faster.\n\n3. High performance. Minimum runtime overhead. See [Boopickle benchmarks](http://ochrons.github.io/boopickle-perftest/) for comparison with other similar libraries.\n\n\n# Usage\n\nAdd Pushka dependency to your project.\n\n```scala\n// For Scala.js\nlibraryDependencies += \"com.github.fomkin\" %%% \"pushka-json\" % \"0.8.0\"\n\n// For Scala.jvm\nlibraryDependencies += \"com.github.fomkin\" %% \"pushka-json\" % \"0.8.0\"\n```\nPushka uses macro annotations which implemented in macro paradise plugin. Unfortunately, it can't be added transitively by Pushka dependency, so you need to plug it manually.\n\n```scala\naddCompilerPlugin(\"org.scalamacros\" % \"paradise\" % \"2.1.0\" cross CrossVersion.full)\n```\nLet's define types we want to write to JSON.\n\n```scala\nimport pushka.annotation._\n\n@pushka case class User(\n  email: String,\n  name: Option[String],\n  role: Role\n)\n\n@pushka sealed trait Role\n\nobject Role {\n  case object Moderator extends Role\n  case object Accountant extends Role\n  case class Group(xs: Seq[Role]) extends Role\n}\n```\nOk. Now let's create data and write it into JSON\n\n```scala\nimport pushka.json._\n\nval data = User(\n  email = \"john@example.com\",\n  name = None,\n  role = Role.Accountant\n)\n\nprintln(write(data))\n```\n\n```json\n{\n  \"email\": \"john@example.com\",\n  \"role\": \"accountant\"\n}\n```\n\nOk. Change user's role.\n\n```scala\ndata.copy(role = Role.Group(Role.Accountant, Role.Moderator))\n```\n```json\n{\n  \"email\": \"john@example.com\",\n  \"role\": {\n    \"group\": [\"accountant\", \"moderator\"]\n  }  \n}\n```\nAdd user name.\n```scala\ndata.copy(name = Some(\"Jonh Doe\"))\n```\n```json\n{\n  \"email\": \"john@example.com\",\n  \"name\": \"John Doe\",\n  \"role\": {\n    \"group\": [\"accountant\", \"moderator\"]\n  }  \n}\n```\n\nNow, in the opposite direction. Let's read JSON.\n\n```scala\nval json = \"\"\"\n  {\n    \"email\": \"john@example.com\",\n    \"name\": \"John Doe\",\n    \"role\": {\n      \"group\": [\"accountant\", \"moderator\"]\n    }  \n  }\n\"\"\"\n\nassert {\n  read[User](json) == User(\n    email = \"john@example.com\",\n    name = Some(\"Jonh Doe\"),\n    role = Role.Group(Role.Accountant, Role.Moderator)\n  )\n}    \n```\n\n### Case class default parameters\n\nWhat if we add the new field to class and try to read JSON written to KV storage with an old version of the class? An exception will be thrown. To avoid this behavior add the new field with a default value.\n\n```scala\n@pushka case class User(\n  email: String,\n  name: Option[String],\n  role: Role,\n  photoUrl: String = \"http://example.com/images/users/dafault.jpg\"\n)\n```\n\n### `@key` annotation\n\nPushka allows to define the key that a field is serialized with via a `@key` annotation.\n\n```scala\n@pushka\ncase class Log(@key(\"@ts\") timestamp: String, message: String)\n```\n\n### `@forceObject` annotation\n\nCase classes with one field are written without object wrapper by default. To avoid this behavior use `@forceObject` annotation.\n\n```scala\n@pushka case class Id(value: String)\nwrite(Id(\"9f3ce5\")) // \"9f3ce5\"\n\n@pushka @forceObject case class Id(value: String)\nwrite(Id(\"9f3ce5\")) // { \"value\": \"9f3ce5\" }\n```\n\n### `Map` writing\n\nObviously `Map[K, V]` should be written as `{}` and this is true when `K` is `String`, `Int`, `Double` or `UUID`. But several `K` types can't be written as JSON object key. Consider `case class Point(x: Int, y: Int)`. This type will be written to JSON object. In this case `Map[Point, T]` will be written as a sequence of tuples.\n\n```scala\n@pushka case class Point(x: Int, y: Int)\n\nval m: Map[Point, String] = Map(\n  Point(0,1) -\u003e \"John\",\n  Point(1,0) -\u003e \"Jane\"\n)\n\nwrite(m)\n```\n```json\n[\n  [ { \"x\": 1, \"y\": 0 }, \"John\" ],\n  [ { \"x\": 0, \"y\": 1 }, \"Jane\" ]\n]\n```\n\nIf you want to write such maps as `{}` you should prove that `K` type can be written as string.\n\n```scala\n@pushka case class Point(x: Int, y: Int)\n\nobject Point {\n  implicit val pointOk = new puska.ObjectKey[Point] {\n    def stringify(value: Point): String = s\"${value.x}:${value.y}\"\n    def parse(s: String): Point = {\n      val Array(x, y) = s.split(\":\")\n      Point(x.toInt, y.toInt)\n    }\n  }\n}\n\nval m: Map[Point, String] = Map(\n  Point(0,1) -\u003e \"John\",\n  Point(1,0) -\u003e \"Jane\"\n)\n\nwrite(m)\n```\n```json\n{\n  \"0:1\": \"John\",\n  \"1:0\": \"Jane\"\n}\n```\n\n### Custom readers and writers\n\nSometimes we want to write objects in a special way. Just define your own reader/writer for your type.\n\n```scala\nimport pushka.RW\nimport pushka.Ast\n\ncase class Name(first: String, last: String)\n\nobject Name {\n  val Pattern = \"(.*) (.*)\".r\n  implicit val rw = new pushka.RW[Name] {\n    def write(value: Name): Ast = {\n      Ast.Str(s\"${value.first} ${value.last}\")\n    }\n    def read(ast: Ast): Name = ast match {\n      case Ast.Str(Pattern(first, last)) =\u003e Name(first, last)\n      case _ =\u003e throw new Exception(\"It's wrong!\")  \n    }\n  }\n}\n\n// ...\n\nwrite(User(\"John\", \"Doe\"))\n```\n```json\n\"John Doe\"\n```\n\n# License\n\nCode released under Apache 2.0 license. See [LICENSE](https://github.com/fomkin/pushka/blob/develop/LICENSE).\n","funding_links":[],"categories":["Table of Contents","JSON"],"sub_categories":["JSON"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffomkin%2Fpushka","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffomkin%2Fpushka","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffomkin%2Fpushka/lists"}