Skip to content

SCALA-400 Add code for zio-json article #1375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,10 @@ lazy val zio2 = (project in file("zio-2"))
name := "zio-2",
scalaVersion := scala3Version,
libraryDependencies += "dev.zio" %% "zio" % zioVersion,
libraryDependencies += "dev.zio" %% "zio-json" % "0.6.2",
libraryDependencies += "dev.zio" %% "zio-test" % zioVersion % Test,
libraryDependencies += "dev.zio" %% "zio-test-sbt" % zioVersion % Test,
libraryDependencies ++= scalaTestDeps,
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.baeldung.scala.zio.json

import zio.json.*

sealed trait Command
case class Start(timeout: Long) extends Command
case object Stop extends Command
case class Kill(reason: String, force: Boolean) extends Command

@jsonDiscriminator("type")
sealed trait Command2
case class Start2(timeout: Long) extends Command2
case object Stop2 extends Command2
case class Kill2(reason: String, force: Boolean) extends Command2

object Start {
implicit val encoder: JsonEncoder[Start] = DeriveJsonEncoder.gen[Start]
implicit val decoder: JsonDecoder[Start] = DeriveJsonDecoder.gen[Start]
}

object Command {
implicit val encoder: JsonEncoder[Command] = DeriveJsonEncoder.gen[Command]
implicit val decoder: JsonDecoder[Command] = DeriveJsonDecoder.gen[Command]
}

object Command2 {
implicit val encoder: JsonEncoder[Command2] = DeriveJsonEncoder.gen[Command2]
implicit val decoder: JsonDecoder[Command2] = DeriveJsonDecoder.gen[Command2]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.baeldung.scala.zio.json

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import zio.json.*

class DecodingUnitTest extends AnyWordSpec with Matchers {
"zio-json" should {
"decode a JSON object to a case class" in {
"""{"timeout":789}""".fromJson[Start] shouldBe Right(Start(789))
}

"return a decoding error if the JSON is not correct" in {
"""{"duration":789}""".fromJson[Start] shouldBe Left(".timeout(missing)")
}

"decode a JSON object with extra fields to a case class" in {
"""{"timeout":789, "extra": "field"}""".fromJson[Start] shouldBe Right(
Start(789)
)
}

"decode an ADT to JSON" in {
"""{"Start":{"timeout":100}}""".fromJson[Command] shouldBe Right(
Start(100)
)
"""{"Stop":{}}""".fromJson[Command] shouldBe Right(Stop)
"""{"Kill":{"reason":"Random reason","force":false}}"""
.fromJson[Command] shouldBe Right(
Kill("Random reason", false)
)
}

"decode Stop to JSON" in {
implicit val decoder: JsonDecoder[Stop.type] =
implicitly[JsonDecoder[String]].map(_ => Stop)

"""{"Start":{"timeout":100}}""".fromJson[Command] shouldBe Right(
Start(100)
)
""""Stop"""".fromJson[Stop.type] shouldBe Right(Stop)
"""{"Kill":{"reason":"Random reason","force":false}}"""
.fromJson[Command] shouldBe Right(
Kill("Random reason", false)
)
}

"use a discriminator" in {
"""{"type":"Start2","timeout":100}""".fromJson[Command2] shouldBe Right(
Start2(100)
)
"""{"type":"Stop2"}""".fromJson[Command2] shouldBe Right(Stop2)
"""{"type":"Kill2","reason":"Random reason","force":false}"""
.fromJson[Command2] shouldBe Right(
Kill2("Random reason", false)
)
}

"fail if there's no discriminator" in {
"""{"timeout":100}""".fromJson[Command2] shouldBe Left(
"(missing hint 'type')"
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.baeldung.scala.zio.json

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import zio.json.*

class EncodingUnitTest extends AnyWordSpec with Matchers {
"zio-json" should {
"encode a case class to JSON" in {
Start(100).toJson shouldBe """{"timeout":100}"""
}

"encode an ADT to JSON" in {
(Start(100): Command).toJson shouldBe """{"Start":{"timeout":100}}"""
(Stop: Command).toJson shouldBe """{"Stop":{}}"""
(Kill(
reason = "Random reason",
force = false
): Command).toJson shouldBe """{"Kill":{"reason":"Random reason","force":false}}"""
}

"encode Stop to JSON" in {
implicit val encoder: JsonEncoder[Stop.type] =
implicitly[JsonEncoder[String]].contramap(_.toString())

(Start(100): Command).toJson shouldBe """{"Start":{"timeout":100}}"""
Stop.toJson shouldBe """"Stop""""
(Kill(
reason = "Random reason",
force = false
): Command).toJson shouldBe """{"Kill":{"reason":"Random reason","force":false}}"""
}

"use a discriminator" in {
(Start2(
100
): Command2).toJson shouldBe """{"type":"Start2","timeout":100}"""
(Stop2: Command2).toJson shouldBe """{"type":"Stop2"}"""
(Kill2(
reason = "Random reason",
force = false
): Command2).toJson shouldBe """{"type":"Kill2","reason":"Random reason","force":false}"""
}
}
}