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)
}
第二序列化。net.liftweb.json.MappingException: No usable value for name.
我怎么可能读取数据形式json到case类,但如果一些字段丢失,它们被替换为case类的默认值?
看起来这已经有一段时间了:https://www.assembla.com/spaces/liftweb/tickets/534
同时,一种选择是使用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)
这应该给你:
User(1,None,None)
你可以用这样的东西来填充默认值:
val r2 = Serialization.read[User](s2).defaults
// r2: User = User(1,Some(John Doe),Some(M))
另一个选择是为case类使用额外的构造函数:
case class User(id: Int, name: String, gender: String)
object User {
def apply(id:Int): User = User(id, "John Doe", "M")
}
如何使用play json库做到这一点,尽管,您必须在解析器中提供默认值,而不仅仅是默认值:
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}"""))
我猜你可以通过创建User的模板实例来提供默认参数,然后将每个字段的默认值传递给Reads.pure
,而不是在那里硬编码字符串。
这里有一个Play解决方案,它不需要您两次指定默认值或在某些奇怪的地方指定默认值-它使用宏在编译时查找适当的默认值。
第一个case类:
case class User(id: Int = 0, name: String = "John Doe", gender: String = "M")
接下来你需要定义我在这篇博文中描述的DefaultFinder
。那么你基本上就完成了:
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
))
最后:
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),)
请注意,我没有使用getOrElse
,因为宏不支持它,但它可以很容易地变得更通用——这只是一个快速的概念验证。
大多数scala JSON库都阻塞在这上面。
我们使用内部的JSON库,该库使用宏为丢失的字段提供相同的默认值。(也区分Null为none和Undefined为默认值。所以你可以有一个默认值为Some的选项,如果你想的话)
希望尽快开放源代码。
编辑:基本上,我不知道有其他人这样做。但这是一个不断变化的生态系统,我很好奇是否有人已经解决了这个问题