0

check out the following example:

object MyTrait {
  def apply(value: Int): MyTrait = value match {
    case 0 => A
    case 1 => B
    case 2 => C
    case _ => C
  }

  def unapply(arg: MyTrait): Int = arg.value
}

case object A extends MyTrait {
  val value = 0
}

case object B extends MyTrait {
  val value = 1
}

case object C extends MyTrait {
  val value = 2
}

case class Test(value: MyTrait)

implicit val testFormatter = Json.format[Test]

all right, this code won't work because i didn't create a JSON serializer for my type "MyTrait". I could do it the way the Play Framework show us to do in the documentation, but it would make a json looking like

{ value: { whatever: 1 } }

and i would like it to look like

{ value: 1 }

In short, i would like my MyTrait objetcs to be interpreted as primitive types (Int) instead of a nested Json Oject.

If someone could help me with that i would be greatful. Thank you by advance!

1
  • You do not provide the Format or Writes for MyTrait in the question (as-is it cannot compile), neither the trait itself. Commented Sep 16, 2020 at 8:06

1 Answer 1

1

The documentation indicates how to provide custom Writes, there to MyTrait to serialize only it's inner value.

sealed trait MyTrait {
  def value: Int
}

case object A extends MyTrait {
  val value = 0
}

case object B extends MyTrait {
  val value = 1
}

case object C extends MyTrait {
  val value = 2
}

object MyTrait {
  import play.api.libs.json._

  def apply(value: Int): MyTrait = value match {
    case 0 => A
    case 1 => B
    case 2 => C
    case _ => C
  }

  def unapply(arg: MyTrait): Int = arg.value

  // ---v
  implicit val writes: Writes[MyTrait] =
    Writes[MyTrait] { v => JsNumber(v.value) }
}

Then when serializing MyTrait instances (note the type ascription bellow is required as Writes is invariant):

Json.toJson(A: MyTrait)
// --> res2: play.api.libs.json.JsValue = 0

And so about Test class:

case class Test(value: MyTrait)

object Test {
  import play.api.libs.json._

  implicit val writes: OWrites[Test] = Json.writes[Test]
}

Json.toJson(Test(A))
// ---> res1: play.api.libs.json.JsValue = {"value":0}

I would rather recommend to have a look at Enumeratum for the enumerated type MyTrait, or to refactor MyTrait as a Value class and use Json.valueFormat as below.

import play.api.libs.json._

final class MyTrait private(val value: Int) extends AnyVal

object MyTrait {
  val A = new MyTrait(0)
  val B = new MyTrait(1)
  val C = new MyTrait(2)

  implicit val format: Format[MyTrait] = Json.valueFormat[MyTrait]
}

case class Test(value: MyTrait)

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

scala> Json.toJson(Test(MyTrait.A))
res0: play.api.libs.json.JsValue = {"value":0}
Sign up to request clarification or add additional context in comments.

1 Comment

hi, now that i see the answer it seems so obvious, why did't i think about that. Thank you very much anyway for the very clear answer !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.