播放2 JSON格式中缺少属性的默认值

 冒泡鱼的快乐2011 发布于 2023-02-10 09:58

我在play scala中有一个等效的以下模型:

case class Foo(id:Int,value:String)
object Foo{
  import play.api.libs.json.Json
  implicit val fooFormats = Json.format[Foo]
}

对于以下Foo实例

Foo(1, "foo")

我会得到以下JSON文档:

{"id":1, "value": "foo"}

此JSON是持久存储的,并从数据存储区读取.现在我的要求已经改变了,我需要为Foo添加一个属性.该属性具有默认值:

case class Foo(id:String,value:String, status:String="pending")

写入JSON不是问题:

{"id":1, "value": "foo", "status":"pending"}

但是从它读取会产生一个JsError,错过了"/ status"路径.

如何以最小的噪音提供默认值?

(ps:我有一个答案,我将在下面发布,但我对此并不满意,并且会赞成并接受任何更好的选择)

4 个回答
  • 我发现最干净的方法是使用"或纯",例如,

    ...      
    ((JsPath \ "notes").read[String] or Reads.pure("")) and
    ((JsPath \ "title").read[String] or Reads.pure("")) and
    ...
    

    当默认值为常量时,可以以常规隐式方式使用此方法.当它是动态的,那么你需要编写一个方法来创建Reads,然后在范围内引入它,a la

    implicit val packageReader = makeJsonReads(jobId, url)
    

    2023-02-10 10:00 回答
  • 玩2.6

    根据@ CanardMoussant的回答,从Play 2.6开始,play-json宏已得到改进,并提出了多个新功能,包括在反序列化时使用默认值作为占位符:

    implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]
    

    对于低于2.6的游戏,最佳选择仍然使用以下选项之一:

    玩-JSON-EXTRA

    我发现了一个更好的解决方案来解决我在play-json中遇到的大多数缺点,包括问题中的一个:

    play-json-extra在内部使用[play-json-extensions]来解决这个问题中的特定问题.

    它包含一个宏,它将自动包含序列化器/解串器中缺少的默认值,使得重构更不容易出错!

    import play.json.extra.Jsonx
    implicit def jsonFormat = Jsonx.formatCaseClass[Foo]
    

    您可能想要检查的库有更多:play-json-extra

    Json变形金刚

    我目前的解决方案是创建一个JSON Transformer并将其与宏生成的Reads相结合.变换器通过以下方法生成:

    object JsonExtensions{
      def withDefault[A](key:String, default:A)(implicit writes:Writes[A]) = __.json.update((__ \ key).json.copyFrom((__ \ key).json.pick orElse Reads.pure(Json.toJson(default))))
    }
    

    然后格式定义变为:

    implicit val fooformats: Format[Foo] = new Format[Foo]{
      import JsonExtensions._
      val base = Json.format[Foo]
      def reads(json: JsValue): JsResult[Foo] = base.compose(withDefault("status","bidon")).reads(json)
      def writes(o: Foo): JsValue = base.writes(o)
    }
    

    Json.parse("""{"id":"1", "value":"foo"}""").validate[Foo]
    

    确实会生成一个应用了默认值的Foo实例.

    我认为这有两个主要缺陷:

    默认密钥名称是字符串,不会被重构所取代

    默认值是重复的,如果在一个地方更改,则需要在另一个地方手动更改

    2023-02-10 10:00 回答
  • 另一种解决方案是formatNullable[T]inmapfrom 结合使用InvariantFunctor.

    import play.api.libs.functional.syntax._
    import play.api.libs.json._
    
    implicit val fooFormats = 
      ((__ \ "id").format[Int] ~
       (__ \ "value").format[String] ~
       (__ \ "status").formatNullable[String].inmap[String](_.getOrElse("pending"), Some(_))
      )(Foo.apply, unlift(Foo.unapply))
    

    2023-02-10 10:00 回答
  • 我认为官方的答案现在应该是使用Play Json 2.6中的WithDefaultValues:

    implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]
    

    编辑:

    值得注意的是,该行为与play-json-extra库不同.例如,如果你有一个DateTime参数,其默认值为DateTime.Now,那么你现在将得到进程的启动时间 - 可能不是你想要的 - 而使用play-json-extra你有时间创建来自JSON.

    2023-02-10 10:03 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有