producing json from case classes
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 class
es
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.