【问题标题】:How to define implicits for Map of two classes?如何为两个类的 Map 定义隐式?
【发布时间】:2020-05-26 19:52:38
【问题描述】:
case class Apple(id:String, name:String)
case class Fruit(id:String,ftype:String)

case class Basket(b:Map[Fruit,Apple])

如何定义playimplicits,下面的定义是不够的。

 implicit val format: Format[Fruit] = Json.format
 implicit val format: Format[Apple] = Json.format

这不起作用:

 implicit val format: Format[Basket] = Json.format

【问题讨论】:

    标签: json scala parsing playframework play-json


    【解决方案1】:

    格式化程序没问题,但它们仅适用于案例类

    所以你所要做的就是调整它们:

    case class Apple(id:String, name:String)
    case class Fruit(id:String,ftype:String)
    
    case class Basket(b:Map[Fruit,Apple])
    

    更新

    好的,还有另一个问题。 JSON 有一个限制,即 MapKey 必须是 字符串

    在这里查看我的答案:https://stackoverflow.com/a/53896463/2750966

    好的,这里是 Play 的示例:

      implicit val formata: Format[Apple] = Json.format
    
      implicit val mapReads: Reads[Map[Fruit, Apple]] = (jv: JsValue) =>
        JsSuccess(jv.as[Map[String, Apple]].map { case (k, v) =>
          (k.split("::").toList match {
            case id :: ftype :: _ => Fruit(id, ftype)
            case other => throw new IllegalArgumentException(s"Unexpected Fruit Key $other")
          }) -> v
        })
    
      implicit val mapWrites: Writes[Map[Fruit, Apple]] = (map: Map[Fruit, Apple]) =>
        Json.toJson(map.map { case (fruit, o) =>
          s"${fruit.id}::${fruit.ftype}" -> o
        })
      implicit val jsonMapFormat: Format[Map[Fruit, Apple]] = Format(mapReads, mapWrites)
    
      implicit val formatb: Format[Basket] = Json.format
    

    使用此示例数据可以正常工作:

      val basket = Basket(Map(Fruit("12A", "granate") -> Apple("A11", "The Super Apple"),
         Fruit("22A", "gala") -> Apple("A21", "The Gala Premium Apple")))
      val json = Json.toJson(basket) // >> {"b":{"12A::granate":{"id":"A11","name":"The Super Apple"},"22A::gala":{"id":"A21","name":"The Gala Premium Apple"}}}
      json.as[Basket] // >> Basket(Map(Fruit(12A,granate) -> Apple(A11,The Super Apple), Fruit(22A,gala) -> Apple(A21,The Gala Premium Apple)))
    

    这里是Scalafiddle

    【讨论】:

    • 对不起,原来是case类,没用。
    • 查看我的更新 - 抱歉我没有发现 - 也许这个例外会帮助解决这个问题。
    • 所以,如果我们需要key作为case类/对象,我们是否应该使用case类的toString方法作为key,这是一个好习惯@pme
    • 从 play-json 2.8 开始,不再需要 Map 的键必须是字符串。您只需要为该类定义一个KeyWritesKeyReads
    • 另外,请注意宏也适用于普通类,而不仅仅是案例类。唯一的要求是伴随对象具有匹配的 apply 和 unapply 方法(案例类会自动实现)。
    【解决方案2】:

    这是使用 Play JSON 2.8 的可能实现:

    import play.api.libs.json._
    
    case class Apple(id:String, name:String)
    case class Fruit(id:String,ftype:String)
    
    case class Basket(b:Map[Fruit,Apple])
    
    object Apple {
      implicit val format = Json.format[Apple]
    }
    
    object Basket {
      implicit val keyReads: KeyReads[Fruit] = s => ???
      implicit val keyWrites: KeyWrites[Fruit] = f => s"${f.id}/${f.ftype}"
    
      implicit val format = Json.format[Basket]
    }
    
    val b = Basket(Map(Fruit("1", "sw") -> Apple("2", "boskop")))
    
    Json.stringify(Json.toJson(b)) // -> {"b":{"1/sw":{"id":"2","name":"boskop"}}}
    

    https://scastie.scala-lang.org/XbFY6amKSb6BCVCrGmgrBQ

    【讨论】:

      猜你喜欢
      • 2016-05-15
      • 1970-01-01
      • 2019-10-29
      • 2020-03-29
      • 2015-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多