Skip to content

Commit

Permalink
more read cases implement
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Zoller committed Mar 28, 2024
1 parent b140b52 commit db8a60d
Show file tree
Hide file tree
Showing 15 changed files with 854 additions and 516 deletions.
10 changes: 5 additions & 5 deletions benchmark/src/main/scala/co.blocke/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ object RunMe extends App:
import co.blocke.scalajack.ScalaJack.*
import co.blocke.scalajack.*

// given codec: JsonValueCodec[Who.Type] = JsonCodecMaker.make

// implicit val blah: ScalaJack[co.blocke.scalajack.json.run.Yippy] = sj[co.blocke.scalajack.json.run.Yippy]

// println( ScalaJack[co.blocke.scalajack.json.run.Yippy].toJson(yippy) )
//case class MapHolder( m: Map[Pet2, String])
// val mh = MapHolder( Map(Pet2("Mindy","Frenchie",4)->"a", Pet2("Rosie","Terrier",8)->"b"))
// given codec: JsonValueCodec[Pet2] = JsonCodecMaker.make
// given codec2: JsonValueCodec[MapHolder] = JsonCodecMaker.make
// println(writeToString(mh))

println("\nDone")
486 changes: 345 additions & 141 deletions src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,23 @@ case class JsonSource(js: CharSequence):
if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator but found $here", this)
fieldNameMatrix.first(bs)

@tailrec
final def parseMap[K, V](kf: () => K, vf: () => V, acc: Map[K, V], isFirst: Boolean = true): Map[K, V] = // initial '{' already consumed
readToken() match
case '}' => acc
case ',' =>
val key = kf()
expectToken(':')
val value = vf()
parseMap[K, V](kf, vf, acc + (key -> value), false)
case _ if isFirst =>
retract()
val key = kf()
expectToken(':')
val value = vf()
parseMap[K, V](kf, vf, acc + (key -> value), false)
case c => throw JsonParseError(s"Expected either object end '}' or field separator ',' here but got '$c'", this)

// Array and Tuple...
// =======================================================

Expand Down
12 changes: 6 additions & 6 deletions src/main/scala/co.blocke.scalajack/run/Play.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ object RunMe extends App:
// val c: Pizza = ScalaJack[Pizza].fromJson("\"READY\"")
// println("Pizza: " + c)

// case class Group(t: (Int, String, Boolean))
implicit val blah: ScalaJack[Group] = sjCodecOf[Group]
val g = Group((5, "Greg", true))
val js = ScalaJack[Group].toJson(g)
println(js)
println(ScalaJack[Group].fromJson(js))
opaque type OnOff = Boolean
opaque type Counter = Short

val m: Map[OnOff, Counter] = Map(true.asInstanceOf[OnOff] -> 1.asInstanceOf[Counter])
val n: Map[Boolean, Short] = m.asInstanceOf[Map[Boolean, Short]]
println(n)

println("done.")
Original file line number Diff line number Diff line change
Expand Up @@ -17,118 +17,118 @@ class MapSpec() extends AnyFunSpec with JsonMatchers:
describe(colorString("+++ Positive Tests +++")) {
it("Map is null must work") {
val inst = MapHolder[Int, Int](null)
val js = sj[MapHolder[Int, Int]].toJson(inst)
val sj = sjCodecOf[MapHolder[Int, Int]]
val js = sj.toJson(inst)
js should matchJson("""{"a":null}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map key of string must work") {
val inst = MapHolder[String, Int](Map("x" -> 1, "y" -> 2))
val js = sj[MapHolder[String, Int]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, Int]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"x":1,"y":2}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map key of long must work") {
val inst = MapHolder[Long, Int](Map(15L -> 1, 25L -> 2))
val js = sj[MapHolder[Long, Int]].toJson(inst)
val sj = sjCodecOf[MapHolder[Long, Int]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"15":1,"25":2}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map key of boolean must work") {
val inst = MapHolder[Boolean, Int](Map(true -> 1, false -> 2))
val js = sj[MapHolder[Boolean, Int]].toJson(inst)
val sj = sjCodecOf[MapHolder[Boolean, Int]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"true":1,"false":2}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map key of uuid must work") {
val inst = MapHolder[UUID, String](Map(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03") -> "x", UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008") -> "y"))
val js = sj[MapHolder[UUID, String]].toJson(inst)
val sj = sjCodecOf[MapHolder[UUID, String]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""")
sj.fromJson(js) shouldEqual(inst)
}

it("Map value of string must work") {
val inst = MapHolder[String, String](Map("w" -> "x", "y" -> "z"))
val js = sj[MapHolder[String, String]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, String]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"w":"x","y":"z"}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map value of long must work") {
val inst = MapHolder[String, Long](Map("w" -> 3L, "y" -> 4L))
val js = sj[MapHolder[String, Long]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, Long]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"w":3,"y":4}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map value of boolean must work") {
val inst = MapHolder[String, Boolean](Map("w" -> true, "y" -> false))
val js = sj[MapHolder[String, Boolean]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, Boolean]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"w":true,"y":false}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map key and value of opaque alias type must work") {
val inst = MapHolder[OnOff, Counter](Map(true.asInstanceOf[OnOff] -> 1.asInstanceOf[Counter], false.asInstanceOf[OnOff] -> 0.asInstanceOf[Counter]))
val sj = sjCodecOf[MapHolder[OnOff, Counter]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"true":1,"false":0}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map value of uuid must work") {
val inst = MapHolder[String, UUID](Map("x" -> UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"), "y" -> UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008")))
val js = sj[MapHolder[String, UUID]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, UUID]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map value of Seq must work") {
val inst = MapHolder[String, List[Int]](Map("w" -> List(1, 2), "y" -> List(3, 4)))
val js = sj[MapHolder[String, List[Int]]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, List[Int]]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map value of Map (nested) must work") {
val inst = MapHolder[String, Map[String, Int]](Map("w" -> Map("r" -> 3, "t" -> 4), "y" -> Map("s" -> 7, "q" -> 9)))
val js = sj[MapHolder[String, Map[String, Int]]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, Map[String, Int]]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""")
sj.fromJson(js) shouldEqual(inst)
}
it("Map value of union type must work") {
val inst = MapHolder[String, Int | List[String]](Map("w" -> 3, "y" -> List("wow", "blah")))
val sj = sjCodecOf[MapHolder[String, Int | List[String]]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""")
sj.fromJson(js) shouldEqual(inst)
}
/*
* Keys of value class (Distance) must work
it("Map value of class must work") {
val inst = MapHolder[String, Person](Map("w" -> Person("Bob", 34), "y" -> Person("Sally", 25)))
val js = sj[MapHolder[String, Person]].toJson(inst)
js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""")
}
it("Map value of union type must work") {
val inst = MapHolder[String, Int | List[String]](Map("w" -> 3, "y" -> List("wow", "blah")))
val js = sj[MapHolder[String, Int | List[String]]].toJson(inst)
js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""")
}
it("Map value of value class must work") {
val inst = MapHolder[String, Distance](Map("w" -> new Distance(1.23), "y" -> Distance(4.56)))
val js = sj[MapHolder[String, Distance]].toJson(inst)
js should matchJson("""{"a":{"w":1.23,"y":4.56}}""")
}
it("Mutable Map value must work") {
val inst = MMapHolder[String, Distance](scala.collection.mutable.HashMap("w" -> new Distance(1.23), "y" -> Distance(4.56)))
val js = sj[MMapHolder[String, Distance]].toJson(inst)
val sj = sjCodecOf[MapHolder[String, Distance]]
val js = sj.toJson(inst)
js should matchJson("""{"a":{"w":1.23,"y":4.56}}""")
sj.fromJson(js) shouldEqual(inst)
}

it("Enum as Map key and value must work") {
val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red))
val js = sj[MapHolder[Color, Color]].toJson(inst)
js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""")
}
it("Enum as Map key and value must work (using id)") {
val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red))
val js = sj[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst)
js should matchJson("""{"a":{"0":2,"1":0}}""")
}
it("Enumeration as Map key and value must work") {
import Permissions.*
val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE))
val js = sj[MapHolder[Permissions, Permissions]].toJson(inst)
js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""")
}
it("Enumeration as Map key and value must work (using id)") {
import Permissions.*
val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE))
val js = sj[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst)
js should matchJson("""{"a":{"0":1,"2":3}}""")
}
it("Java Enumeration as Map key and value must work") {
val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA))
val js = sj[MapHolder[CarEnum, CarEnum]].toJson(inst)
js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""")
}
it("Java Enumeration as Map key and value must work (using id)") {
val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA))
val js = sj[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst)
js should matchJson("""{"a":{"1":2,"2":0}}""")
}
it("Enum/Enumeration mix of enum as value must work") {
import Permissions.*
val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE))
val js = sj[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(Some(List("co.blocke.scalajack.json.collections.Color")))).toJson(inst)
js should matchJson("""{"a":{"0":"WRITE","2":"NONE"}}""")
}
*/
}

// describe(colorString("--- Negative Tests ---")) {
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import java.util.{ArrayList, Map as JMap, Set as JSet}

case class Person(name: String, age: Int)

opaque type OnOff = Boolean
opaque type Counter = Short

case class SeqHolder[T](a: Seq[T])
case class SetHolder[T](a: Set[T])
case class ArrayHolder[T](a: Array[T])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package co.blocke.scalajack
package json
package collections

import ScalaJack.*
import co.blocke.scala_reflection.*
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers.*
import org.scalatest.*
import TestUtil.*

import java.util.UUID

class TupleSpec() extends AnyFunSpec with JsonMatchers:

describe(colorString("-------------------------------\n: Tuple Tests :\n-------------------------------", Console.YELLOW)) {
describe(colorString("+++ Positive Tests +++")) {
it("Tuple is null must work") {
val inst = TupleHolder[Int, String, Boolean](null)
val sj = sjCodecOf[TupleHolder[Int, String, Boolean]]
val js = sj.toJson(inst)
js should matchJson("""{"a":null}""")
sj.fromJson(js) shouldEqual inst
}
it("Tuple of simple types must work") {
val inst = TupleHolder[Int, String, Boolean]((15, "wow", true))
val sj = sjCodecOf[TupleHolder[Int, String, Boolean]]
val js = sj.toJson(inst)
js should matchJson("""{"a":[15,"wow",true]}""")
sj.fromJson(js) shouldEqual inst
}
// it("Tuple of collecitons (including another tuple) must work") {
// val inst = TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]((List(1, 2), Map("a" -> 3L, "b" -> 4L), (1.23d, 'X', true)))
// val js = sjCodecOf[TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]].toJson(inst)
// js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""")
// }
}

describe(colorString("--- Negative Tests ---")) {
it("Wrong number of elements in a tuple") {
val js = """{"a":[15,"wow",true,12.34]}"""
val msg =
"""Expected ']' here at position [19]
|{"a":[15,"wow",true,12.34]}
|-------------------^""".stripMargin
val ex = intercept[JsonParseError](sjCodecOf[TupleHolder[Int, String, Boolean]].fromJson(js))
ex.show shouldEqual msg
}
it("Wrong type of elements in tuple") {
val js = """{"a":[15,true,true]}"""
val msg =
"""Expected a String value but got 't' at position [9]
|{"a":[15,true,true]}
|---------^""".stripMargin
val ex = intercept[JsonParseError](sjCodecOf[TupleHolder[Int, String, Boolean]].fromJson(js))
ex.show shouldEqual msg
}
}
}

This file was deleted.

54 changes: 54 additions & 0 deletions src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
TBD

Test:

* Enumeration
* Enum
* Java Enumeration
* Sealed trait w/case object
* Sealed trait w/case classes

* Try permutations of Map having keys of each of the above types, including ordinal (int) values where available


Partial:


it("Enum as Map key and value must work") {
val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red))
val js = sj[MapHolder[Color, Color]].toJson(inst)
js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""")
}
it("Enum as Map key and value must work (using id)") {
val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red))
val js = sj[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst)
js should matchJson("""{"a":{"0":2,"1":0}}""")
}
it("Enumeration as Map key and value must work") {
import Permissions.*
val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE))
val js = sj[MapHolder[Permissions, Permissions]].toJson(inst)
js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""")
}
it("Enumeration as Map key and value must work (using id)") {
import Permissions.*
val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE))
val js = sj[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst)
js should matchJson("""{"a":{"0":1,"2":3}}""")
}
it("Java Enumeration as Map key and value must work") {
val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA))
val js = sj[MapHolder[CarEnum, CarEnum]].toJson(inst)
js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""")
}
it("Java Enumeration as Map key and value must work (using id)") {
val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA))
val js = sj[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst)
js should matchJson("""{"a":{"1":2,"2":0}}""")
}
it("Enum/Enumeration mix of enum as value must work") {
import Permissions.*
val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE))
val js = sj[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(Some(List("co.blocke.scalajack.json.collections.Color")))).toJson(inst)
js should matchJson("""{"a":{"0":"WRITE","2":"NONE"}}""")
}
Loading

0 comments on commit db8a60d

Please sign in to comment.