ProductPromotion
Logo

Scala

made by https://0x3d.site

GitHub - fomkin/pushka: ABANDONED Pure Scala serialization library with annotations
ABANDONED Pure Scala serialization library with annotations - fomkin/pushka
Visit Site

GitHub - fomkin/pushka: ABANDONED Pure Scala serialization library with annotations

GitHub - fomkin/pushka: ABANDONED Pure Scala serialization library with annotations

Pushka

Build Status Join the chat at https://gitter.im/fomkin/pushka

Pushka is a serialization library implemented without any runtime reflection. It created to reach well human readability of output JSON and good performance. Pushka works well both on Scala (2.10, 2.11, 2.12) and Scala.js.

Motivation

  1. The most of serialization libraries write case classes "as is". For example, if we have Option value it will be written with some kind of wrapper. In the case of sealed traits, most libraries write metadata: trait name and case class name. This makes JSON unreadable by human and makes it useless for creating public API. We want to achieve high human readability of output JSON: no wrappers if possible, no metadata ever.

  2. Codebase simplicity. In our work, we encountered that some libraries based on implicit macroses (including shapeless based) fails on our data. In this project, we want to make the code as simple as possible to find bugs more faster.

  3. High performance. Minimum runtime overhead. See Boopickle benchmarks for comparison with other similar libraries.

Usage

Add Pushka dependency to your project.

// For Scala.js
libraryDependencies += "com.github.fomkin" %%% "pushka-json" % "0.8.0"

// For Scala.jvm
libraryDependencies += "com.github.fomkin" %% "pushka-json" % "0.8.0"

Pushka uses macro annotations which implemented in macro paradise plugin. Unfortunately, it can't be added transitively by Pushka dependency, so you need to plug it manually.

addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)

Let's define types we want to write to JSON.

import pushka.annotation._

@pushka case class User(
  email: String,
  name: Option[String],
  role: Role
)

@pushka sealed trait Role

object Role {
  case object Moderator extends Role
  case object Accountant extends Role
  case class Group(xs: Seq[Role]) extends Role
}

Ok. Now let's create data and write it into JSON

import pushka.json._

val data = User(
  email = "[email protected]",
  name = None,
  role = Role.Accountant
)

println(write(data))
{
  "email": "[email protected]",
  "role": "accountant"
}

Ok. Change user's role.

data.copy(role = Role.Group(Role.Accountant, Role.Moderator))
{
  "email": "[email protected]",
  "role": {
    "group": ["accountant", "moderator"]
  }  
}

Add user name.

data.copy(name = Some("Jonh Doe"))
{
  "email": "[email protected]",
  "name": "John Doe",
  "role": {
    "group": ["accountant", "moderator"]
  }  
}

Now, in the opposite direction. Let's read JSON.

val json = """
  {
    "email": "[email protected]",
    "name": "John Doe",
    "role": {
      "group": ["accountant", "moderator"]
    }  
  }
"""

assert {
  read[User](json) == User(
    email = "[email protected]",
    name = Some("Jonh Doe"),
    role = Role.Group(Role.Accountant, Role.Moderator)
  )
}    

Case class default parameters

What if we add the new field to class and try to read JSON written to KV storage with an old version of the class? An exception will be thrown. To avoid this behavior add the new field with a default value.

@pushka case class User(
  email: String,
  name: Option[String],
  role: Role,
  photoUrl: String = "http://example.com/images/users/dafault.jpg"
)

@key annotation

Pushka allows to define the key that a field is serialized with via a @key annotation.

@pushka
case class Log(@key("@ts") timestamp: String, message: String)

@forceObject annotation

Case classes with one field are written without object wrapper by default. To avoid this behavior use @forceObject annotation.

@pushka case class Id(value: String)
write(Id("9f3ce5")) // "9f3ce5"

@pushka @forceObject case class Id(value: String)
write(Id("9f3ce5")) // { "value": "9f3ce5" }

Map writing

Obviously Map[K, V] should be written as {} and this is true when K is String, Int, Double or UUID. But several K types can't be written as JSON object key. Consider case class Point(x: Int, y: Int). This type will be written to JSON object. In this case Map[Point, T] will be written as a sequence of tuples.

@pushka case class Point(x: Int, y: Int)

val m: Map[Point, String] = Map(
  Point(0,1) -> "John",
  Point(1,0) -> "Jane"
)

write(m)
[
  [ { "x": 1, "y": 0 }, "John" ],
  [ { "x": 0, "y": 1 }, "Jane" ]
]

If you want to write such maps as {} you should prove that K type can be written as string.

@pushka case class Point(x: Int, y: Int)

object Point {
  implicit val pointOk = new puska.ObjectKey[Point] {
    def stringify(value: Point): String = s"${value.x}:${value.y}"
    def parse(s: String): Point = {
      val Array(x, y) = s.split(":")
      Point(x.toInt, y.toInt)
    }
  }
}

val m: Map[Point, String] = Map(
  Point(0,1) -> "John",
  Point(1,0) -> "Jane"
)

write(m)
{
  "0:1": "John",
  "1:0": "Jane"
}

Custom readers and writers

Sometimes we want to write objects in a special way. Just define your own reader/writer for your type.

import pushka.RW
import pushka.Ast

case class Name(first: String, last: String)

object Name {
  val Pattern = "(.*) (.*)".r
  implicit val rw = new pushka.RW[Name] {
    def write(value: Name): Ast = {
      Ast.Str(s"${value.first} ${value.last}")
    }
    def read(ast: Ast): Name = ast match {
      case Ast.Str(Pattern(first, last)) => Name(first, last)
      case _ => throw new Exception("It's wrong!")  
    }
  }
}

// ...

write(User("John", "Doe"))
"John Doe"

License

Code released under Apache 2.0 license. See LICENSE.

More Resources
to explore the angular.

mail [email protected] to add your project or resources here 🔥.

Related Articles
to learn about angular.

FAQ's
to learn more about Angular JS.

mail [email protected] to add more queries here 🔍.

More Sites
to check out once you're finished browsing here.

0x3d
https://www.0x3d.site/
0x3d is designed for aggregating information.
NodeJS
https://nodejs.0x3d.site/
NodeJS Online Directory
Cross Platform
https://cross-platform.0x3d.site/
Cross Platform Online Directory
Open Source
https://open-source.0x3d.site/
Open Source Online Directory
Analytics
https://analytics.0x3d.site/
Analytics Online Directory
JavaScript
https://javascript.0x3d.site/
JavaScript Online Directory
GoLang
https://golang.0x3d.site/
GoLang Online Directory
Python
https://python.0x3d.site/
Python Online Directory
Swift
https://swift.0x3d.site/
Swift Online Directory
Rust
https://rust.0x3d.site/
Rust Online Directory
Scala
https://scala.0x3d.site/
Scala Online Directory
Ruby
https://ruby.0x3d.site/
Ruby Online Directory
Clojure
https://clojure.0x3d.site/
Clojure Online Directory
Elixir
https://elixir.0x3d.site/
Elixir Online Directory
Elm
https://elm.0x3d.site/
Elm Online Directory
Lua
https://lua.0x3d.site/
Lua Online Directory
C Programming
https://c-programming.0x3d.site/
C Programming Online Directory
C++ Programming
https://cpp-programming.0x3d.site/
C++ Programming Online Directory
R Programming
https://r-programming.0x3d.site/
R Programming Online Directory
Perl
https://perl.0x3d.site/
Perl Online Directory
Java
https://java.0x3d.site/
Java Online Directory
Kotlin
https://kotlin.0x3d.site/
Kotlin Online Directory
PHP
https://php.0x3d.site/
PHP Online Directory
React JS
https://react.0x3d.site/
React JS Online Directory
Angular
https://angular.0x3d.site/
Angular JS Online Directory