- Notifications
You must be signed in to change notification settings - Fork9
Fast JSON parser/generator for Scala
License
gzoller/ScalaJack
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ScalaJack 8 is an all-new ScalaJack serializer implemenation built on Scala 3. For Scala 2.13 ScalaJack, please use the frozen version 6.2.0. ScalaJack 8 is builtusing Scala 3.4.2 on JDK 21 LTS version. This is done to be as current as possible and also because Scala 3.4.2 provides improvements to code test coverage instrumentation.
ScalaJack is a very fast, seamless serialization engine for non-schema data designed to require a bare minimum of extra codeto serialize a class. ScalaJack currently only supports JSON, however when we looked at adding MsgPack support to our great surprise benchmarksshowed that MsgPack serialization had about 25% slower write performance and 45% slower read performance than JSON, so we're sticking with JSON for the time being.
ScalaJack is extremely simple to use.
Include the following in your build.sbt:
libraryDependencies ++= Seq("co.blocke" %% "scalajack" % SJ_VERSION)
Now you're good to go! Let's use ScalaJack in your project to serialize/deserialize a case class object into JSON:
// File1.scalacaseclassPerson(name:String,age:Int)// File2.scalaimportco.blocke.scalajack.*givensjPerson:ScalaJack[Person]= sjCodecOf[Person]// create a re-usable Person codec...valinst=Person("Mike",34)valjs= sjPerson.toJson(inst)// """{"name":"Mike","age":34}"""sjPerson.fromJson(js)// re-constitutes original Person
Couldn't be simpler!
|NOTE: Classes must be defined in a different file from where ScalaJack is called.| This is a Scala macro requirement, not a ScalaJack limitation.
Compared to pre-8.0 ScalaJack, which used Scala 2.x runtime reflection, ScalaJack is dramatically faster in almost every case. How does this work? ScalaJack 8 uses compile-time macros to generate all the serialization code for you (the codecs). It's very much like writing hand-tooled, field-by-field serialization code yourself, except ScalaJack does it at compile-time. Wherever you seesjCodecOf
is where the compiler will generate all the serialization code.(That also means you should try not to use sjCodecOf more than once for any given class or you'll generate a lot of redundant code!)
You only need to worry about generating codecs for your top-most level classes. Some serialization libraries require all nested classes in an object hierarchy to bespecifically called out for codec generation, which can get pretty burdensome. ScalaJack doesn't require this. For example:
caseclassDog(name:String,numLegs:Int)caseclassPerson(name:String,age:Int,dog:Dog)// create a re-usable Person codec (includes Dog for free!)givensjPerson:ScalaJack[Person]= sjCodecOf[Person]
In this example, the contained Dog class is automatically detected and genrated by ScalaJack, so if all you care about is Person, and would never serialize a Dog as a top-level value, then Persion is the only codec you need.
ScalaJack 8 uses Scala 3 macros to the fullest extent possible to do the hard work of reflecting on types. Macros impact the compile/test cycle in ways that are non-intuitive at first. Think of this example:
// File1.scalacaseclassFoo(name:String)// File2.scalagivensjFoo:ScalaJack[Foo]= sjCodecOf[Foo]valjs= sjFoo.fromJson(someJson)
In a non-macro program (e.g. something using Scala 2 runtime reflection) let's say you add a new field to class Foo in File1.scala. You naturally expect sbt to re-compile this file, and anything that depends on Foo, and the changes will be picked up in your program, and all will be well.
That'snot necessarily what happens with macros! Remember, the macro code is run/expnded at compile-time. File2.scala needs to be re-compiled because the macro that gets expanded at sjCodecOf[Foo] needs to be re-generated to pick up your changes to Foo class in File1.scala.Unfortunately sbt can't detect this dependency! If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and you'll get a spectacular exception with exotic errors that won't mean much to you. The simple, but non-intuitive, solution is you need to also recompile File2.scala.
This means you will be doing more re-compiling with macro-based code than you would without the macros. It's an unfortunate cost of inconvenience, but the payoff is adramatic gain in speed at runtime, and in the case of reflection in Scala 3, using macros is the only way to accomplish reflection, so there really isn't an alternative.
- Case Classes and Traits
- Non-Case Classes and Java Class Support
- Re-name Case Class Fields
- Any Support
- Value Class Support
- Parameterized Classes
- Trait Type Hint Customization
- Null and None treatment
- NeoType Support
- Union type
- Gimme Speed!
- 8.0.0 -- Rebuild on Scala 3.4.2 and deep refactor of ScalaJack 7.0
- 7.0.3 -- Rebuild on Scala 3.2.1
- 7.0.1 -- GA release of ScalaJack 7 for Scala 3.
- 7.0.0-M2 -- Initial release for Scala 3
About
Fast JSON parser/generator for Scala