Reputation: 8901
I'm having some trouble writing a Format object for some JSON in Play 2.1 Scala. My (greatly simplified) JSON looks like this:
{
"id": "id-1",
"data": {
"foo": "teststr1",
"bar": "teststr2"
}
}
I want to serialize/deserialize this to a slightly simpler, flattened format:
case class TestData(id: String, foo: String, bar: String)
My initial attempt at this looks like so:
implicit val testCaseFormat: Format[TestData] = (
(__ \ "id").format[String] and
(__ \ "data" \ "foo").format[String] and
(__ \ "data" \ "bar").format[String]
)(TestData.apply, unlift(TestData.unapply))
That works fine when reading the data, but when writing only the last "data" value (bar
) exists in the output JSON, presumably because the data
object is being overwritten for each subsequent value:
{
"id" : "id-1",
"data":{
"bar":"teststr2"
}
}
Is there a correct way to do this flattening and unflattening, without resorting to separate Reads
and Writes
objects? My actual data structures are quite large (although not too complex) so conciseness is good.
Upvotes: 0
Views: 495
Reputation: 11244
If I understand correctly you could/should create your format in a regular fashion:
(__ \ "id").format[String] and
(__ \ "data").format(
(__ \ "foo").format[String] and
(__ \ "bar").format[String]
tupled
)
And then just supply different apply
and unapply
methods.
def specialApply(data:(String, (String, String))) = {
val(id, (foo, bar)) = data
TestData(id, foo, bar)
}
def specialUnapply(data:TestData):Option[(String, (String, String))] =
Option(data) map { testData =>
(testData.id, (testData.foo, testData.bar))
}
You can avoid adding the methods to the companion objects by composing the apply and unapply methods in place:
implicit val testCaseFormat: Format[TestData] = (
// ...
)(TestData.tupled.compose { data:(String, (String, String)) =>
val (id, (foo, bar)) = data
(id, foo, bar)
}), unlift(TestData.unapply))
Upvotes: 2