1
votes

When using spray-json, I need to bring a JsonFormat[A] into implicit scope for every domain type A that I want to serialize.

The recommended approach is to create a custom object with all the implicits as fields:

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val colorFormat = jsonFormat4(Color)
}

import MyJsonProtocol._

My app has a great many domain types, some of which have long names. My MyJsonProtocol is getting long and unreadable:

object MyJsonProtocol extends DefaultJsonProtocol {
  ... // many more here
  implicit val someClassWithALongNameFormat = jsonFormat4(SomeClassWithALongName)
  implicit val someClassWithALongNameInnerFormat = jsonFormat4(SomeClassWithALongNameInner)
  implicit val someClassWithALongNameVariantBFormat = jsonFormat4(SomeClassWithALongNameVariantB)
  ... // many more here
}

The long val names have various problems:

  • they feel redundant (the names are never read)
  • they make my lines very long
  • they introduce a copy/paste risk that the name of the format won't match the type of the format
  • they make the RHS values not aligned, which hides the common pattern here

Is there any way to bring an object into implicit scope without naming it? Something like this would be much neater:

object MyJsonProtocol extends DefaultJsonProtocol {
  ... // many more here
  implicit val _ = jsonFormat4(SomeClassWithALongName)
  implicit val _ = jsonFormat4(SomeClassWithALongNameInner)
  implicit val _ = jsonFormat4(SomeClassWithALongNameVariantB)
  ... // many more here
}

... but Scala doesn't allow multiple fields named "_".

Is there any way to bring these formats into implicit scope without naming them all? Is there another way to use spray-json that avoids this issue?

1

1 Answers

3
votes

Normally, I define typeclass instances in the companion objects:

case class Foo()
object Foo {
  implicit val jsonFormatter = new JsonFormat[Foo] { ... }
}

case class Bar()
object Bar {
  implicit val jsonFormatter = new JsonFormat[Bar] { ... }
}

I don't have to import anything, as companion objects are by default included in the implicit search scope, and the implicit members can all have the same names.