I posted previously about consuming JSON using the Scala json4s library.

Today I learned something new about that library when trying to produce JSON.

The situation: I want to produce json where each top-level key points to a JSON Array that was created from a List of case classes

To be specific I want to convert the following Scala:

case class BreakfastItem(name: String)
case class LunchItem(name: String)

val breakfastItemList = List(BreakfastItem("oj"), BreakfastItem("oatmeal"))
val lunchItemList = List(LunchItem("pbj"), LunchItem("chips"))

to this JSON:

{
  "breakfast": [
    {
      "name": "oj"
    },
    {
      "name": "oatmeal"
    }
  ],
  "lunch": [
    {
      "name": "pbj"
    },
    {
      "name": "chips"
    }
  ]
}

(for example’s sake, please imagine the case classes in use have more than just a single field…)

First..imports are important! Esp. the JsonDSL one for this example.

import org.json4s._
import org.json4s.JsonDSL._

// and don't forget to declare this in scope of the json4s library calls:
implicit val jsonFormats: Formats = DefaultFormats

Using the docs, I would expect the following Scala code to produce the intended JSON using the json4s ~ operator:

case class BreakfastItem(name: String)
case class LunchItem(name: String)

val breakfastItemList = List(BreakfastItem("oj"), BreakfastItem("oatmeal"))
val lunchItemList = List(LunchItem("pbj"), LunchItem("chips"))

("breakfast" -> breakfastItemList) ~ ("lunch" -> lunchItemList)

However it fails with the following error:

Error:(89, 40) value ~ is not a member of (String, List[com.uptake.uptown.resources.BreakfastItem])

So what is going on here? It seems the library does not know how to serialize our case class.

This can be confirmed by trying the same approach using a list of primitive strings instead of case classes:

("breakfast" -> List("oj", "oatmeal")) ~ ("lunch" -> List("pbj", "chips"))

That code checks out just fine. So what’s the fix?

The answer lies in the json4s Extraction object. Its decompose() method will “Decompose a case class into JSON.”

Usage:

("breakfast" -> Extraction.decompose(breakfastItemList)) ~
  ("lunch" -> Extraction.decompose(lunchItemList))

This will produce the intended output.