https://github.com/paoloboni/spray-json-derived-codecs
Derived codecs for spray-json for Scala 3.x and 2.x
https://github.com/paoloboni/spray-json-derived-codecs
auto-derivation derived-codecs polymorphic-types recursive-types spray-json
Last synced: 26 days ago
JSON representation
Derived codecs for spray-json for Scala 3.x and 2.x
- Host: GitHub
- URL: https://github.com/paoloboni/spray-json-derived-codecs
- Owner: paoloboni
- License: apache-2.0
- Created: 2020-12-25T17:56:38.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2024-05-05T00:30:05.000Z (almost 2 years ago)
- Last Synced: 2024-05-05T01:28:00.339Z (almost 2 years ago)
- Topics: auto-derivation, derived-codecs, polymorphic-types, recursive-types, spray-json
- Language: Scala
- Homepage:
- Size: 200 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# spray-json derived codecs
[](https://github.com/paoloboni/spray-json-derived-codecs/actions?query=workflow)
[](https://search.maven.org/artifact/io.github.paoloboni/spray-json-derived-codecs_2.13)
[](http://codecov.io/github/paoloboni/spray-json-derived-codecs?branch=master)
[](https://scala-steward.org)
[](https://opensource.org/licenses/Apache-2.0)
`JsonFormat` derivation for algebraic data types, inspired by [Play Json Derived Codecs](https://github.com/julienrf/play-json-derived-codecs).
The derivation built with Scala 2.x is powered by [shapeless](https://github.com/milessabin/shapeless), whereas the one built with Scala 3 is based on the new [Type Class Derivation](https://dotty.epfl.ch/docs/reference/contextual/derivation.html) language API.
The derivation currently supports:
- sum types
- product types
- recursive types
- polymorphic types
This library is built with Sbt 1.5.2 or later, and its master branch is built with Scala 2.13.6 by default but also cross-builds for 3 and 2.12.
---
**NOTE**
Scala 2.11 is no longer supported. The latest version available for scala 2.11 is 2.2.2.
---
## Installation
If you use sbt add the following dependency to your build file:
```sbtshell
libraryDependencies += "io.github.paoloboni" %% "spray-json-derived-codecs" % ""
```
## Usage
For automatic derivation, add the following import:
```scala
import spray.json.derived.auto._
```
If you prefer to explicitly define your formats, then you can use semi-auto derivation:
```scala
import spray.json.derived.semiauto._
```
### Examples
#### Product types
##### Auto derivation
```scala
import spray.json._
import spray.json.derived.auto._
import spray.json.DefaultJsonProtocol._
case class Cat(name: String, livesLeft: Int)
object Test extends App {
val oliver: Cat = Cat("Oliver", 7)
val encoded = oliver.toJson
assert(encoded == """{"livesLeft":7,"name":"Oliver"}""".parseJson)
assert(encoded.convertTo[Cat] == oliver)
}
```
##### Semi-auto derivation
```scala
import spray.json._
import spray.json.derived.semiauto._
import spray.json.DefaultJsonProtocol._
case class Cat(name: String, livesLeft: Int)
object Test extends App {
implicit val format: JsonFormat[Cat] = deriveFormat[Cat]
val oliver: Cat = Cat("Oliver", 7)
val encoded = oliver.toJson
assert(encoded == """{"livesLeft":7,"name":"Oliver"}""".parseJson)
assert(encoded.convertTo[Cat] == oliver)
}
```
#### Union types
Union types are encoded by using a discriminator field, which by default is `type`.
```scala
import spray.json._
import spray.json.derived.auto._
import spray.json.DefaultJsonProtocol._
sealed trait Pet
case class Cat(name: String, livesLeft: Int) extends Pet
case class Dog(name: String, bonesHidden: Int) extends Pet
object Test extends App {
val oliver: Pet = Cat("Oliver", 7)
val encodedOliver = oliver.toJson
assert(encodedOliver == """{"livesLeft":7,"name":"Oliver","type":"Cat"}""".parseJson)
assert(encodedOliver.convertTo[Pet] == oliver)
val albert: Pet = Dog("Albert", 3)
val encodedAlbert = albert.toJson
assert(encodedAlbert == """{"bonesHidden":3,"name":"Albert","type":"Dog"}""".parseJson)
assert(encodedAlbert.convertTo[Pet] == albert)
}
```
The discriminator can be customised by annotating the union type with the `@Discriminator` annotation:
```scala
import spray.json._
import spray.json.derived.auto._
import spray.json.DefaultJsonProtocol._
import spray.json.derived.Discriminator
@Discriminator("petType")
sealed trait Pet
case class Cat(name: String, livesLeft: Int) extends Pet
case class Dog(name: String, bonesHidden: Int) extends Pet
object Test extends App {
val oliver: Pet = Cat("Oliver", 7)
val encodedOliver = oliver.toJson
assert(encodedOliver == """{"livesLeft":7,"name":"Oliver","petType":"Cat"}""".parseJson)
assert(encodedOliver.convertTo[Pet] == oliver)
}
```
#### Recursive types
```scala
import spray.json._
import spray.json.derived.auto._
import spray.json.DefaultJsonProtocol._
sealed trait Tree
case class Leaf(s: String) extends Tree
case class Node(lhs: Tree, rhs: Tree) extends Tree
object Test extends App {
val obj: Tree = Node(Node(Leaf("1"), Leaf("2")), Leaf("3"))
val encoded = obj.toJson
val expectedJson =
"""{
| "lhs": {
| "lhs": {
| "s": "1",
| "type": "Leaf"
| },
| "rhs": {
| "s": "2",
| "type": "Leaf"
| },
| "type": "Node"
| },
| "rhs": {
| "s": "3",
| "type": "Leaf"
| },
| "type": "Node"
|}
|""".stripMargin
assert(encoded == expectedJson.parseJson)
assert(encoded.convertTo[Tree] == obj)
}
```
#### Polymorphic types
```scala
import spray.json._
import spray.json.derived.auto._
import spray.json.DefaultJsonProtocol._
case class Container[T](value: T)
object Test extends App {
val cString: Container[String] = Container("abc")
val cStringEncoded = cString.toJson
assert(cStringEncoded == """{"value":"abc"}""".parseJson)
assert(cStringEncoded.convertTo[Container[String]] == cString)
val cInt: Container[Int] = Container(123)
val cIntEncoded = cInt.toJson
assert(cIntEncoded == """{"value":123}""".parseJson)
assert(cIntEncoded.convertTo[Container[Int]] == cInt)
}
```
#### Undefined optional members
By default, undefined optional members are not rendered:
```scala
import spray.json._
import spray.json.derived.auto._
import spray.json.DefaultJsonProtocol._
case class Dog(toy: Option[String])
object Test extends App {
val aDog = Dog(toy = None)
val aDogEncoded = aDog.toJson
assert(aDogEncoded.compactPrint == "{}")
}
```
It's possible to render undefined optional members as null values by specifying an alternative configuration.
Just specify the alternative configuration as implicit value and enable the `renderNullOptions` flag:
```scala
import spray.json._
import spray.json.derived.Configuration
import spray.json.derived.auto._
import spray.json.DefaultJsonProtocol._
case class Dog(toy: Option[String])
object Test extends App {
implicit val conf: Configuration = Configuration(renderNullOptions = true)
val aDog = Dog(toy = None)
val aDogEncoded = aDog.toJson
assert(aDogEncoded.compactPrint == """{"toy":null}""")
}
```
## License
spray-json-derived-codecs is licensed under [APL 2.0](http://www.apache.org/licenses/LICENSE-2.0).