Skip to content

Instantly share code, notes, and snippets.

@dacr
Created January 18, 2025 08:31
Show Gist options
  • Save dacr/38875c7d47925ced57b46733547ac36b to your computer and use it in GitHub Desktop.
Save dacr/38875c7d47925ced57b46733547ac36b to your computer and use it in GitHub Desktop.
jsoniter scala json API cookbook as unit test cases. / published by https://github.com/dacr/code-examples-manager #069fba65-a07f-44b3-9a78-451082a452ab/41240ea553c60c9cdaa22b3e193637f3bface6b6
// summary : jsoniter scala json API cookbook as unit test cases.
// keywords : scala, scalatest, upickle, json, @testable
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 069fba65-a07f-44b3-9a78-451082a452ab
// created-on : 2025-01-14T16:23:47+01:00
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli $file
// ---------------------
//> using scala "3.6.2"
//> using dep "org.scalatest::scalatest:3.2.19"
//> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.33.0
//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.33.0
//> using dep io.circe::circe-generic:0.14.10 // For GenericJson purposes only
//> using dep io.circe::circe-parser:0.14.10 // For GenericJson purposes only
//> using objectWrapper
// ---------------------
import com.github.plokhotnyuk.jsoniter_scala.core.*
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import org.scalatest.*
import matchers.*
import java.time.OffsetDateTime
import java.util.UUID
// ---------------------------------------------------------------------------------------------------------------------
// FOR GENERIC JSON PURPOSES EITHER RAW VALUE OR GENERIC CIRCE JSON AST
// inspired by https://github.com/plokhotnyuk/jsoniter-scala/blob/209d918a030b188f064ee55505a6c47257731b4b/jsoniter-scala-macros/src/test/scala/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMakerSpec.scala#L644-L673
case class RawVal private (bytes: Array[Byte]) extends AnyVal {
override def toString: String = String(bytes, "UTF-8")
}
object RawVal { // for maximum performance
given JsonValueCodec[RawVal] = new JsonValueCodec[RawVal] {
override def decodeValue(in: JsonReader, default: RawVal): RawVal = new RawVal(in.readRawValAsBytes())
override def encodeValue(x: RawVal, out: JsonWriter): Unit = out.writeRawVal(x.bytes)
override val nullValue: RawVal = new RawVal(new Array[Byte](0))
}
}
case class GenericJson private (json: io.circe.Json) extends AnyVal {
override def toString: String = json.noSpaces
}
object GenericJson {
given JsonValueCodec[GenericJson] = new JsonValueCodec[GenericJson] {
override def decodeValue(in: JsonReader, default: GenericJson): GenericJson = {
val parsed = io.circe.parser.parse(String(in.readRawValAsBytes(), "UTF-8"))
parsed match
case Right(value) => new GenericJson(value)
case Left(value) => throw RuntimeException(value.toString) // TODO refactor to remove this horror
}
override def encodeValue(x: GenericJson, out: JsonWriter): Unit = out.writeRawVal(x.json.noSpaces.getBytes("UTF-8"))
override val nullValue: GenericJson = new GenericJson(io.circe.Json.Null)
}
}
// ---------------------------------------------------------------------------------------------------------------------
case class Someone(id: UUID, age: Int, name: String)
case class SomeoneExtended(id: UUID, age: Int, name: String, gender: Option[String])
enum EventCategory {
case Meeting
case Diner
}
case class Event(when: OffsetDateTime, who: Someone, what: String, category: EventCategory)
type PetName = String
sealed trait Pet {
val name: PetName
val birthYear: Int
}
case class Dog(name: PetName, birthYear: Int) extends Pet
case class Cat(name: PetName, birthYear: Int, lifeCount: Int) extends Pet
case class Animals(animals: List[Pet])
class JsoniterCookBook extends flatspec.AnyFlatSpec with should.Matchers {
override def suiteName = "JsonUpickleCookBook"
given JsonValueCodec[Someone] = JsonCodecMaker.make
given JsonValueCodec[SomeoneExtended] = JsonCodecMaker.make
given JsonValueCodec[EventCategory] = JsonCodecMaker.makeWithoutDiscriminator
given JsonValueCodec[Event] = JsonCodecMaker.make
given JsonValueCodec[Pet] = JsonCodecMaker.make
given JsonValueCodec[Dog] = JsonCodecMaker.make
given JsonValueCodec[Cat] = JsonCodecMaker.make
given JsonValueCodec[Animals] = JsonCodecMaker.make
given JsonValueCodec[Array[Int]] = JsonCodecMaker.make
given JsonValueCodec[List[Int]] = JsonCodecMaker.make
given JsonValueCodec[String] = JsonCodecMaker.make
given JsonValueCodec[Int] = JsonCodecMaker.make
given JsonValueCodec[BigDecimal] = JsonCodecMaker.make
val anId = UUID.fromString("babebabe-cafe-cafe-cafe-42babebabe42")
"jsoniter" should "deserialize scala case classes" in {
val decoded = readFromString[Someone](s"""{"age":42, "name":"john", "id":"$anId"}""")
decoded shouldBe Someone(anId, 42, "john")
}
it should "deserialize scala case classes with optional field" in {
val decoded = readFromString[SomeoneExtended](s"""{"age":42, "name":"john", "gender":"male","id":"$anId"}""")
decoded shouldBe SomeoneExtended(anId, 42, "john", Some("male"))
}
it should "supports enumerations" in {
val example =
s"""{
| "when": "2022-03-14T18:31:29+01:00",
| "who": { "id": "$anId", "age": 42, "name": "john" },
| "what": "hello",
| "category": "Diner"
|}""".stripMargin
readFromString[Event](example).category shouldBe EventCategory.Diner
//readFromString[EventCategory](s"""{"category":"Meeting"}""") shouldBe EventCategory.Meeting
}
it should "deserialize json array strings" in {
val decoded = readFromString[Array[Int]]("""[1,2,3]""")
decoded shouldBe Array(1, 2, 3)
}
it should "deserialize scala list" in {
val decoded = readFromString[List[Int]]("""[1,2,3]""")
decoded shouldBe List(1, 2, 3)
}
it should "deserialize literals" in {
readFromString[String](""""hello"""") shouldBe "hello"
readFromString[Int]("""42""") shouldBe 42
readFromString[BigDecimal]("""42""") shouldBe BigDecimal(42)
}
}
org.scalatest.tools.Runner.main(Array("-oDF", "-s", classOf[JsoniterCookBook].getName))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment