{"id":13652923,"url":"https://github.com/spray/spray-json","last_synced_at":"2025-04-08T08:14:29.507Z","repository":{"id":40362877,"uuid":"1713422","full_name":"spray/spray-json","owner":"spray","description":"A lightweight, clean and simple JSON implementation in Scala","archived":false,"fork":false,"pushed_at":"2024-01-10T20:17:55.000Z","size":924,"stargazers_count":972,"open_issues_count":101,"forks_count":190,"subscribers_count":36,"default_branch":"release/1.3.x","last_synced_at":"2025-04-01T05:34:12.526Z","etag":null,"topics":["json","json-parser","json-serialization","scala"],"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/spray.png","metadata":{"files":{"readme":"README.markdown","changelog":"CHANGELOG.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2011-05-06T21:31:38.000Z","updated_at":"2025-03-09T19:27:27.000Z","dependencies_parsed_at":"2024-06-19T02:50:37.148Z","dependency_job_id":"89497401-d61c-4b08-ba73-859095c514b3","html_url":"https://github.com/spray/spray-json","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-json","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-json/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-json/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-json/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spray","download_url":"https://codeload.github.com/spray/spray-json/tar.gz/refs/heads/release/1.3.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247801169,"owners_count":20998339,"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","json-parser","json-serialization","scala"],"created_at":"2024-08-02T02:01:04.034Z","updated_at":"2025-04-08T08:14:29.449Z","avatar_url":"https://github.com/spray.png","language":"Scala","readme":"_spray-json_ is a lightweight, clean and efficient [JSON] implementation in Scala.\n\nIt supports the following features:\n\n* A simple immutable model of the JSON language elements\n* An efficient JSON parser\n* Choice of either compact or pretty JSON-to-string printing\n* Type-class based (de)serialization of custom objects (no reflection, no intrusion)\n* No external dependencies\n\n_spray-json_ allows you to convert between\n * String JSON documents\n * JSON Abstract Syntax Trees (ASTs) with base type JsValue\n * instances of arbitrary Scala types\n\nas depicted in this diagram:\n\n![Spray-JSON conversions](images/Conversions.png \"Conversions possible with Spray-JSON\")\n\n### Installation\n\n_spray-json_ is available from maven central.\n\nLatest release: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.spray/spray-json_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.spray/spray-json_2.12)\n\nIf you use SBT you can include _spray-json_ in your project with\n\n```scala\nlibraryDependencies += \"io.spray\" %%  \"spray-json\" % \"1.3.6\"\n```\n\n### Usage\n\n_spray-json_ is really easy to use.\nJust bring all relevant elements in scope with\n\n```scala\nimport spray.json._\nimport DefaultJsonProtocol._ // if you don't supply your own Protocol (see below)\n```\n\nand do one or more of the following:\n\n1. Parse a JSON string into its Abstract Syntax Tree (AST) representation\n    \n    ```scala\n    val source = \"\"\"{ \"some\": \"JSON source\" }\"\"\"\n    val jsonAst = source.parseJson // or JsonParser(source)\n    ```\n    \n2. Print a JSON AST back to a String using either the `CompactPrinter` or the `PrettyPrinter`\n    \n    ```scala\n    val json = jsonAst.prettyPrint // or .compactPrint\n    ```\n    \n3. Convert any Scala object to a JSON AST using the `toJson` extension method\n    \n    ```scala\n    val jsonAst = List(1, 2, 3).toJson\n    ```\n    \n4. Convert a JSON AST to a Scala object with the `convertTo` method\n    \n    ```scala\n    val myObject = jsonAst.convertTo[MyObjectType]\n    ```\n\nIn order to make steps 3 and 4 work for an object of type `T` you need to bring implicit values in scope that\nprovide `JsonFormat[T]` instances for `T` and all types used by `T` (directly or indirectly).\nThe way you normally do this is via a \"JsonProtocol\".\n\n### JsonProtocol\n\n_spray-json_ uses [SJSON]s Scala-idiomatic type-class-based approach to connect an existing type `T` with the logic how\nto (de)serialize its instances to and from JSON. (In fact _spray-json_ even reuses some of [SJSON]s code, see the\n'Credits' section below).\n\nThis approach has the advantage of not requiring any change (or even access) to `T`s source code. All (de)serialization\nlogic is attached 'from the outside'. There is no reflection involved, so the resulting conversions are fast. Scalas\nexcellent type inference reduces verbosity and boilerplate to a minimum, while the Scala compiler will make sure at\ncompile time that you provided all required (de)serialization logic.\n\nIn _spray-jsons_ terminology a 'JsonProtocol' is nothing but a bunch of implicit values of type `JsonFormat[T]`, whereby\neach `JsonFormat[T]` contains the logic of how to convert instance of `T` to and from JSON. All `JsonFormat[T]`s of a\nprotocol need to be \"mece\" (mutually exclusive, collectively exhaustive), i.e. they are not allowed to overlap and\ntogether need to span all types required by the application.\n\nThis may sound more complicated than it is.\n_spray-json_ comes with a `DefaultJsonProtocol`, which already covers all of Scala's value types as well as the most\nimportant reference and collection types. As long as your code uses nothing more than these you only need the\n`DefaultJsonProtocol`. Here are the types already taken care of by the `DefaultJsonProtocol`:\n\n* Byte, Short, Int, Long, Float, Double, Char, Unit, Boolean\n* String, Symbol\n* BigInt, BigDecimal\n* Option, Either, Tuple1 - Tuple7\n* List, Array\n* immutable.{Map, Iterable, Seq, IndexedSeq, LinearSeq, Set, Vector}\n* collection.{Iterable, Seq, IndexedSeq, LinearSeq, Set}\n* JsValue\n\nIn most cases however you'll also want to convert types not covered by the `DefaultJsonProtocol`. In these cases you\nneed to provide `JsonFormat[T]`s for your custom types. This is not hard at all.\n\n\n### Providing JsonFormats for Case Classes\n\nIf your custom type `T` is a case class then augmenting the `DefaultJsonProtocol` with a `JsonFormat[T]` is really easy:\n\n```scala\ncase class Color(name: String, red: Int, green: Int, blue: Int)\n\nobject MyJsonProtocol extends DefaultJsonProtocol {\n  implicit val colorFormat = jsonFormat4(Color)\n}\n\nimport MyJsonProtocol._\nimport spray.json._\n\nval json = Color(\"CadetBlue\", 95, 158, 160).toJson\nval color = json.convertTo[Color]\n```\n\nThe `jsonFormatX` methods reduce the boilerplate to a minimum, just pass the right one the companion object of your\ncase class and it will return a ready-to-use `JsonFormat` for your type (the right one is the one matching the number\nof arguments to your case class constructor, e.g. if your case class has 13 fields you need to use the `jsonFormat13`\nmethod). The `jsonFormatX` methods try to extract the field names of your case class before calling the more general\n`jsonFormat` overloads, which let you specify the field name manually. So, if spray-json has trouble determining the\nfield names or if your JSON objects use member names that differ from the case class fields you can also use\n`jsonFormat` directly.\n\nThere is one additional quirk: If you explicitly declare the companion object for your case class the notation above will\nstop working. You'll have to explicitly refer to the companion objects `apply` method to fix this:\n\n```scala\ncase class Color(name: String, red: Int, green: Int, blue: Int)\nobject Color\n\nobject MyJsonProtocol extends DefaultJsonProtocol {\n  implicit val colorFormat = jsonFormat4(Color.apply)\n}\n```\n\nIf your case class is generic in that it takes type parameters itself the `jsonFormat` methods can also help you.\nHowever, there is a little more boilerplate required as you need to add context bounds for all type parameters\nand explicitly refer to the case classes `apply` method as in this example:\n\n```scala\ncase class NamedList[A](name: String, items: List[A])\n\nobject MyJsonProtocol extends DefaultJsonProtocol {\n  implicit def namedListFormat[A :JsonFormat] = jsonFormat2(NamedList.apply[A])\n}\n```\n\n\n#### NullOptions\n\nThe `NullOptions` trait supplies an alternative rendering mode for optional case class members. Normally optional\nmembers that are undefined (`None`) are not rendered at all. By mixing in this trait into your custom JsonProtocol you\ncan enforce the rendering of undefined members as `null`.\n(Note that this only affect JSON writing, spray-json will always read missing optional members as well as `null`\noptional members as `None`.)\n\n\n### Providing JsonFormats for other Types\n\nOf course you can also supply (de)serialization logic for types that aren't case classes.\nHere is one way to do it:\n\n```scala\nclass Color(val name: String, val red: Int, val green: Int, val blue: Int)\n\nobject MyJsonProtocol extends DefaultJsonProtocol {\n  implicit object ColorJsonFormat extends RootJsonFormat[Color] {\n    def write(c: Color) =\n      JsArray(JsString(c.name), JsNumber(c.red), JsNumber(c.green), JsNumber(c.blue))\n\n    def read(value: JsValue) = value match {\n      case JsArray(Vector(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue))) =\u003e\n        new Color(name, red.toInt, green.toInt, blue.toInt)\n      case _ =\u003e deserializationError(\"Color expected\")\n    }\n  }\n}\n\nimport MyJsonProtocol._\n\nval json = new Color(\"CadetBlue\", 95, 158, 160).toJson\nval color = json.convertTo[Color]\n```\n\nThis serializes `Color` instances as a JSON array, which is compact but does not make the elements semantics explicit.\nYou need to know that the color components are ordered \"red, green, blue\".\n\nAnother way would be to serialize `Color`s as JSON objects:\n\n```scala\nobject MyJsonProtocol extends DefaultJsonProtocol {\n  implicit object ColorJsonFormat extends RootJsonFormat[Color] {\n    def write(c: Color) = JsObject(\n      \"name\" -\u003e JsString(c.name),\n      \"red\" -\u003e JsNumber(c.red),\n      \"green\" -\u003e JsNumber(c.green),\n      \"blue\" -\u003e JsNumber(c.blue)\n    )\n    def read(value: JsValue) = {\n      value.asJsObject.getFields(\"name\", \"red\", \"green\", \"blue\") match {\n        case Seq(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue)) =\u003e\n          new Color(name, red.toInt, green.toInt, blue.toInt)\n        case _ =\u003e throw new DeserializationException(\"Color expected\")\n      }\n    }\n  }\n}\n```\n\nThis is a bit more verbose in its definition and the resulting JSON but transports the field semantics over to the\nJSON side. Note that this is the approach _spray-json_ uses for case classes.\n\n### Providing JsonFormats for unboxed types\n\nA value class\n\n```scala\ncase class PhoneNumber(value: String) extends AnyVal\nval num = PhoneNumber(\"+1 212 555 1111\")\n```\n\nor a class with multiple members\n\n```scala\ncase class Money(currency: String, amount: BigDecimal)\nval bal = Money(\"USD\", 100)\n```\n\ncan be handled as above with `jsonFormatX`, etc.\nIt may be preferable, however, to serialize such instances without object boxing:\nas `\"USD 100\"` instead of `{\"currency\":\"USD\",\"amount\":100}`.\nThis requires explicit (de)serialization logic:\n\n```scala\nimplicit object MoneyFormat extends JsonFormat[Money] {\n  val fmt = \"\"\"([A-Z]{3}) ([0-9.]+)\"\"\".r\n  def write(m: Money) = JsString(s\"${m.currency} ${m.amount}\")\n  def read(json: JsValue) = json match {\n    case JsString(fmt(c, a)) =\u003e Money(c, BigDecimal(a))\n    case _ =\u003e deserializationError(\"String expected\")\n  }\n}\n```\n\n\n### JsonFormat vs. RootJsonFormat\n\nAccording to the JSON specification not all of the defined JSON value types are allowed at the root level of a JSON\ndocument. A JSON string for example (like `\"foo\"`) does not constitute a legal JSON document by itself.\nOnly JSON objects or JSON arrays are allowed as JSON document roots.\n\nIn order to distinguish, on the type-level, \"regular\" JsonFormats from the ones producing root-level JSON objects or\narrays _spray-json_ defines the [`RootJsonFormat`][1] type, which is nothing but a marker specialization of `JsonFormat`.\nLibraries supporting _spray-json_ as a means of document serialization might choose to depend on a `RootJsonFormat[T]`\nfor a custom type `T` (rather than a \"plain\" `JsonFormat[T]`), so as to not allow the rendering of illegal document\nroots. E.g., the `SprayJsonSupport` trait of _spray-routing_ is one notable example of such a case.\n\nAll default converters in the `DefaultJsonProtocol` producing JSON objects or arrays are actually implemented as\n`RootJsonFormat`. When \"manually\" implementing a `JsonFormat` for a custom type `T` (rather than relying on case class\nsupport) you should think about whether you'd like to use instances of `T` as JSON document roots and choose between\na \"plain\" `JsonFormat` and a `RootJsonFormat` accordingly.\n\n  [1]: http://spray.github.com/spray/api/spray-json/cc/spray/json/RootJsonFormat.html\n\n\n### JsonFormats for recursive Types\n\nIf your type is recursive such as\n\n```scala\ncase class Foo(i: Int, foo: Foo)\n```\n\nyou need to wrap your format constructor with `lazyFormat` and supply an explicit type annotation:\n\n```scala\nimplicit val fooFormat: JsonFormat[Foo] = lazyFormat(jsonFormat(Foo, \"i\", \"foo\"))\n```\n\nOtherwise your code will either not compile (no explicit type annotation) or throw an NPE at runtime (no `lazyFormat`\nwrapper). Note, that `lazyFormat` returns a `JsonFormat` even if it was given a `RootJsonFormat` which means it isn't\npicked up by `SprayJsonSupport`. To get back a `RootJsonFormat` just wrap the complete `lazyFormat` call with another\ncall to `rootFormat`.\n\n\n### Customizing Parser Settings\n\nThe parser can be customized by providing a custom instance of `JsonParserSettings` to `JsonParser.apply` or\n`String.parseJson`:\n\n```scala\nval customSettings =\n  JsonParserSettings.default\n     .withMaxDepth(100)\n     .withMaxNumberCharacters(20)\nval jsValue = JsonParser(jsonString, customSettings)\n// or\nval jsValue = jsonString.parseJson(customSettings)\n```\n\n### Credits\n\nMost of type-class (de)serialization code is nothing but a polished copy of what **Debasish Ghosh** made available\nwith his [SJSON] library. These code parts therefore bear his copyright.\nAdditionally the JSON AST model is heavily inspired by the one contributed by **Jorge Ortiz** to [Databinder-Dispatch].\n\n\n### License\n\n_spray-json_ is licensed under [APL 2.0].\n\n### Mailing list\n\nSpray-json is in primarily \"maintanance mode\", as it contains the basic functionality it is meant to deliver.\nIf you have any questions about it though, please open issues on this repository.\n\n\n### Maintanance mode\n\n_spray-json_ is largely considered feature-complete for the basic functionality it provides.\nIt is currently maintained by the Akka team at Lightbend.\n\nFeedback and contributions to the project, no matter what kind, are always very welcome.\n\nAlong with any patches, please state that the patch is your original work and that you license the work to the\n_spray-json_ project under the project’s open source license.\n\n\n  [JSON]: http://json.org\n  [SJSON]: https://github.com/debasishg/sjson\n  [Databinder-Dispatch]: https://github.com/dispatch/classic\n  [APL 2.0]: http://www.apache.org/licenses/LICENSE-2.0\n","funding_links":[],"categories":["Libraries","Table of Contents","JSON Manipulation","JSON"],"sub_categories":["JSON"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspray%2Fspray-json","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspray%2Fspray-json","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspray%2Fspray-json/lists"}