Created
January 18, 2025 08:31
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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