Search code examples
jsonscalaplayframework-2.2casbahsalat

Format nullable Seq or List of objects with Play Json and Salat


I want to convert a json to Salat model. I am using Play 2.X Scala Json. I couldn't find any documentations to format nullable Seq. According to https://github.com/novus/salat/wiki/SupportedTypes, I can't use Option[Seq] Or Option[List].

The following json is good, but sometimes 'locations' can be missing.

{
    "id": 581407,
    "locations": [
        {
            "id": 1692,
            "tag_type": "LocationTag",
            "name": "san francisco",
            "display_name": "San Francisco"
        }]
}

These are the classes:

case class User(
 var id: Int,
 var locations: Seq[Tag] = Seq.empty
)

case class Tag(
  id: Int,
  tag_type:String,
  name:String,
  display_name:String
)

How can I format nullable 'locations'?

implicit val format: Format[User] = (
    (__ \ 'id).format[Int] and
    (__ \ 'locations).formatNullable(Seq[Tag])
)

Solution

  • Format is an invariant functor, so you can use inmap to change a Option[Seq[Tag]] format into a format for Seq[Tag]:

    import play.api.libs.functional.syntax._
    import play.api.libs.json._
    
    implicit val formatTag: Format[Tag] = Json.format[Tag]
    
    implicit val formatUser: Format[User] = (
      (__ \ 'id).format[Int] and
      (__ \ 'locations).formatNullable[Seq[Tag]].inmap[Seq[Tag]](
        o => o.getOrElse(Seq.empty[Tag]),
        s => if (s.isEmpty) None else Some(s)
      )
    )(User.apply, unlift(User.unapply))
    

    This will not produce a locations value when serializing a user with no locations, but if you want an empty array in that case you can just change the None in the second argument to inmap to Some(Seq.empty).