All Projects → julienrf → Play Json Derived Codecs

julienrf / Play Json Derived Codecs

Licence: mit

Programming Languages

scala
5932 projects

Projects that are alternatives of or similar to Play Json Derived Codecs

Play Json Extra
playframework2 json extra module. provide convenience functions for define Format, Reads, Writes
Stars: ✭ 20 (-88.83%)
Mutual labels:  json, play-framework
Validation
validation api extracted from play
Stars: ✭ 194 (+8.38%)
Mutual labels:  json, play-framework
Web Database Analytics
Web scrapping and related analytics using Python tools
Stars: ✭ 175 (-2.23%)
Mutual labels:  json
School
Sistema de gerenciamento para escola em Laravel com VueJS (Não é mais Angular)
Stars: ✭ 178 (-0.56%)
Mutual labels:  json
Quran Json
Holy Quran with translation and tafsir in Bahasa Indonesia, presented in JSON format
Stars: ✭ 177 (-1.12%)
Mutual labels:  json
Tilejson Spec
JSON format for describing map tilesets.
Stars: ✭ 175 (-2.23%)
Mutual labels:  json
Json table
Flutter package: Json Table Widget to create table from json array
Stars: ✭ 178 (-0.56%)
Mutual labels:  json
Json2html
🐍 Python module for converting complex JSON to HTML Table representation
Stars: ✭ 173 (-3.35%)
Mutual labels:  json
Jsons
🐍 A Python lib for (de)serializing Python objects to/from JSON
Stars: ✭ 178 (-0.56%)
Mutual labels:  json
Django Import Export
Django application and library for importing and exporting data with admin integration.
Stars: ✭ 2,265 (+1165.36%)
Mutual labels:  json
Boo
Boo - A beautiful, clean and responsive theme for Ghost.
Stars: ✭ 176 (-1.68%)
Mutual labels:  json
Json
Strongly typed JSON library for Rust
Stars: ✭ 2,544 (+1321.23%)
Mutual labels:  json
Qxorm
QxOrm library - C++ Qt ORM (Object Relational Mapping) and ODM (Object Document Mapper) library - Official repository
Stars: ✭ 176 (-1.68%)
Mutual labels:  json
Cleanjson
Swift JSON decoder for Codable
Stars: ✭ 178 (-0.56%)
Mutual labels:  json
Live Stream Radio
24/7 live stream video radio station CLI / API 📹 📻
Stars: ✭ 175 (-2.23%)
Mutual labels:  json
Jackson Jq
jq for Jackson Java JSON Processor
Stars: ✭ 178 (-0.56%)
Mutual labels:  json
Githubdb
A Lightweight Cloud based JSON Database with a MongoDB like API for Node.
Stars: ✭ 174 (-2.79%)
Mutual labels:  json
Gulp Ng Config
🔧 Create AngularJS constants from a JSON config file
Stars: ✭ 176 (-1.68%)
Mutual labels:  json
Staticjson
Fast, direct and static typed parsing of JSON with C++
Stars: ✭ 177 (-1.12%)
Mutual labels:  json
Jsome
✨ Make your JSON look AWESOME
Stars: ✭ 179 (+0%)
Mutual labels:  json

(Note: this project has been renamed from play-json-variants to play-json-derived-codecs)

Play JSON Derived Codecs

Reads, OWrites and OFormat derivation for algebraic data types (sealed traits and case classes, possibly recursive), powered by shapeless.

Compared to the built-in macros, this project brings support for:

  • sealed traits ;
  • recursive types ;
  • polymorphic types.

The artifacts are built for Scala and Scala.js 2.12, and 2.13, Play 2.9 and Shapeless 2.3.

For Play 2.8 compatibility see version 7.0.0.

Usage

import julienrf.json.derived

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

object User {
  implicit val reads: Reads[User] = derived.reads
}

The API is simple: the object julienrf.json.derived has just three methods.

  • reads[A], derives a Reads[A] ;
  • owrites[A], derives a OWrites[A] ;
  • oformat[A], derives a OFormat[A].

Representation of Sum Types

By default, sum types (types extending a sealed trait) are represented by a JSON object containing one field whose name is the name of the concrete type and whose value is the JSON object containing the value of the given type.

For instance, consider the following data type:

sealed trait Foo
case class Bar(s: String, i: Int) extends Foo
case object Baz extends Foo

The default JSON representation of Bar("quux", 42) is the following JSON object:

{
  "Bar": {
    "s": "quux",
    "i": 42
  }
}

Configuring the Derivation Process

Three aspects of the derivation process can be configured:

  • the representation of sum types,
  • the way case class field names are mapped to JSON property names,
  • the type name used to discriminate sum types.

Custom Representation of Sum Types

The default representation of sum types may not fit all use cases. For instance, it is not very practical for enumerations.

For instance, you might want to represent the Bar("quux", 42) value as the following JSON object:

{
  "type": "Bar",
  "s": "quux",
  "i": 42
}

Here, the type information is flattened with the Bar members.

You can do so by using the methods in the derived.flat object:

implicit val fooOWrites: OWrites[Foo] =
  derived.flat.owrites((__ \ "type").write[String])

In case you need even more control, you can implement your own TypeTagOWrites and TypeTagReads.

Custom Field Names Mapping

By default, case class fields are mapped to JSON object properties having the same name.

You can transform this mapping by supplying a different NameAdapter parameter. For instance, to use “snake case” in JSON:

implicit val userFormat: OFormat[User] = derived.oformat(adapter = NameAdapter.snakeCase)

Custom Type Names

By default, case class names are used as discriminators (type tags) for sum types.

You can configure the type tags to use by using the derived.withTypeTag object:

implicit val fooFormat: OFormat[Foo] =
  derived.withTypeTag.oformat(TypeTagSetting.FullClassName)

The library provides the following TypeTagSetting values out of the box:

  • TypeTagSetting.ShortClassName: use the class name (as it is defined in Scala)
  • TypeTagSetting.FullClassName: use the fully qualified name
  • TypeTagSetting.UserDefinedName: require the presence of an implicit CustomTypeTag[A] for all type A of the sum type, providing the type tag to use

Custom format for certain types in hierarchy

Sometimes, you might want to represent one type differently than default format would. This can be done by creating an implicit instance of DerivedReads or DerivedWrites for said type. Below is an example of implementing both custom reads and writes for a single class in a hierarchy:

sealed trait Hierarchy
case class First(x: Integer)
case class Second(y: Integer)

implicit val SecondReads: DerivedReads[Second] = new DerivedReads[Second] {
  def reads(tagReads: TypeTagReads, adapter: NameAdapter) = tagReads.reads("Second", (__ \ "foo").read[Integer].map(foo => Second(foo)))
}

implicit val SecondWrites: DerivedOWrites[Second] = new DerivedOWrites[Second] {
  override def owrites(tagOwrites: TypeTagOWrites, adapter: NameAdapter): OWrites[Second] =
    tagOwrites.owrites[Second](
      "Second",
      OWrites[Second](s => JsObject(("foo", Json.toJson(s.y)) :: Nil))
    )
}

val defaultTypeFormat = (__ \ "type").format[String]
implicit val HierarchyFormat = derived.flat.oformat[Hierarchy](defaultTypeFormat)

This will cause Second to be read with SecondReads, and read with SecondWrites.

Avoiding redundant derivation

By default, the auto-derivation mechanism will be applied to the whole sealed hierarchy. This might be costly in terms of compile-time (as Shapeless is being used under the hood). To avoid this, it is possible to define an OFormat for the different cases, thus only using auto-derivation for the branching in the sealed trait and nothing else.

sealed trait Hierarchy
case class First(a: Int, b: Int, c: Int)
case class Second(x: Int, y: Int, c: Int)

object First {
  implicit val format: OFormat[First] = Json.format
}

object Second {
  implicit val format: OFormat[Second] = Json.format
}

implicit val HierarchyFormat = derived.oformat[Hierarchy]

Note: it's important that the provided Formats are of the specific OFormat sub-type, otherwise, they won't be picked up by the implicit machinery.

Without the provided Formats the derivation mechanism will traverse all the fields in the hierarchy (in this case 6 in total), which may be costly for larger case classes.

Providing the implicits this way can also be used for customization without having to deal with supplying your own type-tags.

Contributors

See here.

Changelog

See here.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].