How to fill case class from json with partial data?

import net.liftweb.json._ import net.liftweb.json.JsonParser._ object test02 extends App { implicit val formats = DefaultFormats case class User( id: Int = 0, name: String = "John Doe", gender: String = "M") val s1=""" {"id":1,"name":"Bill","gender":"M"} """ var r1=Serialization.read[User](s1) println(r1) val s2=""" {"id":1} """ var r2=Serialization.read[User](s2) println(r2) } 

The second way Serialization.read throws an exception: net.liftweb.json.MappingException: there is no useful value for the name.

How can I read the json data form in the case class, but if some fields are missing, they are replaced with the default values ​​from the case class?

+7
source share
4 answers

It seems like there was an open ticket for a while: https://www.assembla.com/spaces/liftweb/tickets/534

In the meantime, one option is to use Option :

  case class User( id: Int = 0, name: Option[String], gender: Option[String]) { def defaults = copy( name = name orElse Some("John Doe"), gender = gender orElse Some("M")) } // ... val s2=""" {"id":1} """ var r2=Serialization.read[User](s2) println(r2) 

This should give you:

  User(1,None,None) 

And you can use something like this to populate the default values:

  val r2 = Serialization.read[User](s2).defaults // r2: User = User(1,Some(John Doe),Some(M)) 

Another option is to use additional constructors for your case class:

  case class User(id: Int, name: String, gender: String) object User { def apply(id:Int): User = User(id, "John Doe", "M") } 
+5
source

How to do this with the json playback library, although you need to provide default values ​​in the parser, and not just the default values:

 case class User(id: Int, name: String, gender: String) import play.api.libs.json._ import play.api.libs.functional.syntax._ implicit val userReads: Reads[User] = ( (__ \ "id").read[Int] and (__ \ "name").read[String].or(Reads.pure("John Doe")) and (__ \ "gender").read[String].or(Reads.pure("Male")) )(User) Json.fromJson[User](Json.parse("""{"id":1,"name":"Bill","gender":"M"}""")) Json.fromJson[User](Json.parse("""{"id":1}""")) 

I assume that you could provide default values ​​from the default parameter by creating an instance of the user template and then passing the default from each field to Reads.pure instead of the hardcoding line there.

+5
source

There is a Play solution here that does not require you to specify default values ​​twice or in some strange place - it uses a macro to find the corresponding default at compile time.

First for the case class:

 case class User(id: Int = 0, name: String = "John Doe", gender: String = "M") 

Then you need to define DefaultFinder , as I described in this blog post . Then you practically did:

 import DefaultFinder._ import play.api.libs.json._ import play.api.libs.functional.syntax._ implicit val userReads: Reads[User] = ( (__ \ 'id).readNullable[Int] and (__ \ 'name).readNullable[String] and (__ \ 'gender).readNullable[String] )((id, name, gender) => new User( id = if (id.isEmpty) default else id.get, name = if (name.isEmpty) default else name.get, gender = if (gender.isEmpty) default else gender.get )) 

And finally:

 scala> Json.fromJson[User](Json.parse("""{ "id": 1, "name": "Foo McBar" }""")) res0: play.api.libs.json.JsResult[User] = JsSuccess(User(1,Foo McBar,M),) scala> Json.fromJson[User](Json.parse("""{ "id": 2, "gender": "X" }""")) res1: play.api.libs.json.JsResult[User] = JsSuccess(User(2,John Doe,X),) 

Note that I do not use getOrElse because the macro does not support it, but it can easily be made more general - it was just a brief proof of concept.

+4
source

Most scala JSON libraries are choking on this.

We use the built-in JSON library, which uses macros to provide normal default values ​​for missing fields. (also distinguishes Null as none, and Undefined by default. So you can have an option with a value of Some if you want)

Hope to open it soon.

EDIT: Basically, I don't know the others who do this. but its ever-changing ecosystem, curious to see if anyone else has tackled it.

0
source

All Articles