What's the most elegant way to deconstruct a JSON array from JSON4?
I need to deconstruct the following JSON into a list of case classes:
{ "data": [ [49, true, 14, null, null], [52, false, null, null, null], [72, true, 4, 2, 1] ] }
case class:
case class Data(i1: Int, b: Bool, i2: Option[Int], i3: Option[Int], i4: Option[Int])
I started with understanding but couldn't finish it:
for { JArray(data) <- json \ "data" JArray(d) <- data JInt(line) <- d.head // ??? } yield Data()
Any help is greatly appreciated.
Thank,
Michael
source to share
You can write CustomSerializer
for Data
.
I introduced an extractor JOptionInt
to turn a JInt
or JNull
into Option[Int]
, maybe this can be done directly in json4s.
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.JsonDSL._
case class Data(i1: Int, b: Boolean, i2: Option[Int], i3: Option[Int], i4: Option[Int])
object DataSerializer extends CustomSerializer[Data]( format => (
{
case JArray(List(JInt(i1), JBool(b), JOptionInt(i2), JOptionInt(i3), JOptionInt(i4))) =>
Data(i1.toInt, b, i2, i3 , i4)
}, {
case d: Data => JArray(List(d.i1, d.b, d.i2, d.i3, d.i4))
}
))
object JOptionInt {
def unapply(x: JValue) : Option[Option[Int]] = x match {
case JInt(i) => Option(Option(i.toInt))
case JNull => Option(None)
case _ => None
}
}
which can be used like:
implicit val formats = DataSerializer
val json = parse("""
{
"data": [
[49, true, 14, null, null],
[52, false, null, null, null],
[72, true, 4, 2, 1]
]
}
""")
val result = (json \ "data").extract[Array[Data]]
// Array(Data(49,true,Some(14),None,None), Data(52,false,None,None,None), Data(72,true,Some(4),Some(2),Some(1)))
source to share
If you can allow the Rapture JSON library to be included, it can be done as follows, while still using the JSON4S backend. This requires the following imports:
import rapture.json._, jsonBackends.json4s._
If you already have JSON as JValue
, you can convert it to Rapture type Json
like this:
val json = Json(jValue)
Given your case class definition, you need to override the JSON extractor for types Data
(there is already a default extractor that expects a JSON object), e.g .:
implicit val dataExtractor = Json.extractor[Json].map { j =>
Data(j(0).as[Int], j(1).as[Boolean], j(2).as[Option[Int]],
j(3).as[Option[Int]], j(4).as[Option[Int]])
}
and you can extract it with:
val list = json.as[List[Data]]
source to share