Making Reads and Writes in Scala Play for lists of custom classes
Solution 1
The "No unapply or unapplySeq function found
" error is caused by these two:
implicit val listItemReads = Json.reads[List[Item]]
implicit val listItemWrites = Json.writes[List[Item]]
Just throw them away. As Ende said, Play knows how to deal with lists.
But you need Reads
and Writes
for Order
too! And since you do both reading and writing, it's simplest to define a Format
, a mix of the Reads
and Writes
traits. This should work:
case class Item(id: Int, name: String)
object Item {
implicit val format = Json.format[Item]
}
case class Order(id: Int, items: List[Item])
object Order {
implicit val format = Json.format[Order]
}
Above, the ordering is significant; Item
and the companion object must come before Order
.
So, once you have all the implicit converters needed, the key is to make them properly visible in the controllers. The above is one solution, but there are other ways, as I learned after trying to do something similar.
Solution 2
You don't actually need to define those two implicits, play already knows how to deal with a list:
scala> import play.api.libs.json._
import play.api.libs.json._
scala> case class Item(id: Int, name: String)
defined class Item
scala> case class Order(id: Int, items: List[Item])
defined class Order
scala> implicit val itemReads = Json.reads[Item]
itemReads: play.api.libs.json.Reads[Item] = play.api.libs.json.Reads$$anon$8@478fdbc9
scala> implicit val itemWrites = Json.writes[Item]
itemWrites: play.api.libs.json.OWrites[Item] = play.api.libs.json.OWrites$$anon$2@26de09b8
scala> Json.toJson(List(Item(1, ""), Item(2, "")))
res0: play.api.libs.json.JsValue = [{"id":1,"name":""},{"id":2,"name":""}]
scala> Json.toJson(Order(10, List(Item(1, ""), Item(2, ""))))
res1: play.api.libs.json.JsValue = {"id":10,"items":[{"id":1,"name":""},{"id":2,"name":""}]}
The error you see probably happens because play uses the unapply method to construct the macro expansion for your read/write and List
is an abstract class, play-json needs concrete type to make the macro work.
Solution 3
This works:
case class Item(id: Int, name: String)
case class Order(id: Int, items: List[Item])
implicit val itemFormat = Json.format[Item]
implicit val orderFormat: Format[Order] = (
(JsPath \ "id").format[Int] and
(JsPath \ "items").format[JsArray].inmap(
(v: JsArray) => v.value.map(v => v.as[Item]).toList,
(l: List[Item]) => JsArray(l.map(item => Json.toJson(item)))
)
)(Order.apply, unlift(Order.unapply))
This also allows you to customize the naming for your JSON object. Below is an example of the serialization in action.
Json.toJson(Order(1, List(Item(2, "Item 2"))))
res0: play.api.libs.json.JsValue = {"id":1,"items":[{"id":2,"name":"Item 2"}]}
Json.parse(
"""
|{"id":1,"items":[{"id":2,"name":"Item 2"}]}
""".stripMargin).as[Order]
res1: Order = Order(1,List(Item(2,Item 2)))
I'd also recommend using format
instead of read
and write
if you are doing symmetrical serialization / deserialization.
Related videos on Youtube
Will
Updated on June 04, 2022Comments
-
Will almost 2 years
So I have two classes in my project
case class Item(id: Int, name: String)
and
case class Order(id: Int, items: List[Item])
I'm trying to make reads and writes properties for Order but I get a compiler error saying:
"No unapply or unapplySeq function found"
In my controller I have the following:
implicit val itemReads = Json.reads[Item] implicit val itemWrites = Json.writes[Item] implicit val listItemReads = Json.reads[List[Item]] implicit val listItemWrites = Json.writes[List[Item]]
The code works for
itemReads
anditemWrites
but not for the bottom two. Can anyone tell me where I'm going wrong, I'm new to Play framework.Thank you for your time.
-
Jonik over 8 yearsYou are right in that
Json.reads[List[Item]]
andJson.writes[List[Item]]
are not necessary; those cause the "No unapply or unapplySeq function found" error. However,List
works just fine, no concrete type needed. I think OP's case can be solved by making the implicit converters properly visible, using one of the approaches here: stackoverflow.com/q/34615571/56285 -
Jonik over 8 years@Will: while this works, it is way more complicated than necessary, fortunately! As Ende pointed out, Play already knows how to deal with lists. Try this: stackoverflow.com/a/34618829/56285