简体   繁体   中英

play framework json reads from empty string to empty list

Hi everyone recently I faced an issue in converting json into my own data model.

I have a json format message which may contain an empty string:

{
    "name" : "John Doe", 
    "hobbies": ""
}

or a list of hobby types:

{
    "name" : "John Doe", 
    "hobbies": [{"name":"basketball"}]
}

And the following is my case class data model in scala play framework:

case class Person(name: String, hobbies: List[Hobby])
case class Hobby(name: String)

Right now I'm using the default json formatter but of course it's not working well when we have empty string as value.

implicit val HobbyJson= Json.format[Hobby]
implicit val PersonJson = Json.format[Person]

it will throw exception if the hobbies has a empty string. I want to convert it into an empty list when it's the empty string. I search the document Play provides but couldn't find infomation. Can anyone give some suggestions?

Thanks in advance.

As you mentioned, the default Format macros won't work for you here because of the inconsistent treatment of hobbies . So you need to implement your own Reads[Person] - here's how I'd do it:

object PersonJson {
  implicit val hobbyConverter = Json.format[Hobby]

  val personReads = new Reads[Person] {

    override def reads(json: JsValue): JsResult[Person] = {
      for {
        personName  <- (json \ "name").validate[String]
        hobbies     <- (json \ "hobbies").validate[JsValue]
      } yield {
        val maybeHobbyList = hobbies.validate[List[Hobby]].asOpt
        Person(personName, maybeHobbyList.getOrElse(Nil))
      }
    }
  }

  implicit val personConverter = Format(personReads, Json.writes[Person])
}

The key thing to note here is surrounding the whole thing in a JsResult courtesy of the for-comprehension and the yield . This gives us all the necessary checking (like the name field being there and being a String, and the hobbies field being there).

The code within the yield block only runs if we've got something that looks pretty close to a Person . Then we can safely try validating the hobbies as a List[Hobby] , and convert the result to an Option[List[Hobby]] . It'll be a None if it didn't work (thus it must have been a string) and so we default it to the empty list as required.

Thanks @millhouse answer, it definitely works. Like he said we need a custom Reads[Person] to properly convert it.

I also post my code as reference.

  implicit val personJsonReads: Reads[Person] = (
      (__ \ "name").read[String] and
      (__ \ "hobbies").read[List[Hobby]].orElse(Reads.pure(List())) 
  ) (Person.apply _)

read[List[Hobby]].orElse(Reads.pure(List())) will generate the empty list when the value cannot convert to List[Hobby] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM