Search code examples
scalacirce

How to "flatten" JSON representation of objects using circe i.e., from case class to its string representation?


I have a custom Date class that we use internally:

case class Date(month: Int, day: Int, year: Year)

And use this as so:

case class Person(dateOfBirth: Date)

However, when I generate the Json for Person(Date(12, 20, 1990)) I get something like:

{
 "dateOfBirth": {
  "month": 12,
  "day": 20,
  "year": 1990
 }
}

What I'd like to get is something like this:

{ "dateOfBirth": "12-20-2990" } // or any custom format

Is it possible to "flatten" custom case classes so that they're just treated like a value instead of being expanded out? I've tried something like this and it leads to a StackOverflowError:

  implicit val dateEncoder: Encoder[Date] = (date: Date) => {
    Json.fromString(s"${date.month}-${date.dayOfMonth}-${date.year}")
  }

UPDATE: This error seems unrelated to the encoder - it just happens to get triggered when this encoder is added but not otherwise forcing me to conclude that this is not the right way of encoding. I've accepted the answer since it does correctly answer the "asked" question.

Here's the encoder that "fails" after the Date one is added in:

  implicit val myEncoder: Encoder[Vector[MyCaseClass]] = (my: Vector[MyCaseClass]) => {
    if (my.nonEmpty) my.asJson else Json.Null
  }

I could encode this as an Option[Vector[MyCaseClass]] but I was experimenting with directly encoding a Vector to see what happens...


Solution

  • You can write an encoder/decoder for any type manually. It seems you need a new implementation for Date:

    object Date {
      implicit val encoder: Encoder[Date] = (date: Date) =>
        Json.fromString(s"${date.day}-${date.month}-${date.year}")
      implicit val decoder: Decoder[Date] = ??? // if you need this
    }