【问题标题】:Scala Play - How to format Generics for JSON conversionScala Play - 如何为 JSON 转换格式化泛型
【发布时间】:2015-06-30 10:15:44
【问题描述】:

我越来越多地了解 Scala 和那个不错的游戏框架。但是有些事情困扰着我,我无法上班。

例如,我喜欢将泛型用于某种集合。但我需要将它们以 JSON 格式存储在我们的数据库中。有一个很酷的自动转换的东西,但它不适用于泛型,我从来没有尝试过:-/

好的,具体来说,先写代码:

case class InventorySlot(id: Long, item: Option[Item])

object InventorySlot {
  implicit val fmt = Json.format[InventorySlot]
}


case class Inventory[T <: Item](slots: Vector[InventorySlot]) {
  def length = slots.length

  def items: Vector[T] = slots.map(slot => slot.item).flatten.asInstanceOf[Vector[T]]

  def item(id: Long): Option[T] = {
    slots.find(_.id == id) match {
      case Some(slot: InventorySlot) =>
        Some(slot.item.asInstanceOf[T])
      case None =>
        Logger.warn(s"slot with id $id not found")
        None
    }
  }
}

object Inventory {
  implicit val fmt = Json.format[Inventory]
}

物品是可以放入该库存的不同物品的基本抽象类。没关系。但有时我想要一个仅适用于 ItemType A 的库存,我们称之为AItem。 所以我想用这样的东西创建我的库存: val myInventory = Inventory[AItem]("content of vector here") 和当我调用 myInventory.item(2) 时,我想在插槽 2 中获取项目,它应该是 AItem 类型的对象,而不仅仅是 Item。 (这就是我在这里使用泛型的原因)

所以问题

Inventory 的隐式格式显然不起作用。 Item 可以,还有所有特殊物品,我可以在下面发布代码,InventorySlot 应该也可以。

编译时的错误是:

Error:(34, 34) Play 2 Compiler: 
 C:\depot\mars\mainline\server\app\models\Test.scala:34: class Inventory takes type parameters
   implicit val fmt = Json.format[Inventory]
                                  ^

我试图写出显式的读写,比如

implicit val fmt = (
  (__ \ "slots").format[Vector[InventorySlot]]
  )(Inventory.apply, unlift(Inventory.unapply))

wich 甚至无法在我的 IDE 中运行,而且我找不到问题所在。 我很迷惑。我不知道我的错误在哪里,或者我做错了什么,或者我只是错过了什么。

任何帮助将不胜感激。

我很无奈,我什至考虑过为所有可能的库存类型做一个类,比如

case class AItemInventory(protected var slots: Vector[InventorySlot]) extends Inventory[AItem](slots)

object AItemInventory {
  implicit val fmt = Json.format[AItemInventory]
}

非常有效。没有问题,一切都很好。所以……我不明白。如果它看起来完全一样,只是硬编码,为什么它会起作用?

附录

项目格式化程序,非常有效:

implicit val itemFormat = new Format[Item] {
  override def reads(json: JsValue): JsResult[Item] = {
    (json \ "itemType").as[ItemType] match {
      case ItemType.AITEM => fmtAItem.reads(json)
    }
  }

  override def writes(item: Item): JsValue = item match {
    case subItem: AItem => fmtAItem.writes(subItem)
    case _ => JsNumber(item.itemType.id)
  }
}

【问题讨论】:

  • 你的解决方案应该在 play 2.5 中工作吗?下面链接的文档适用于 2.0,2.5 文档中没有等效页面。 (json \ "value") match {case JsArray(s) =&gt; Some(s)} 在 JsArray 处给我一个编译错误,说 JsookupResult required not JsArray

标签: json scala generics playframework format


【解决方案1】:
object Inventory {
  implicit def fmt[T <: Item](implicit fmt: Format[T]): Format[Inventory[T]] = new Format[Inventory[T]] {
    def reads(json: JsValue): Inventory[T] = new Inventory[T] (
      (json \ "blah").as[String]
    )
    def writes(i: Inventory[T]) = JsObject(Seq(
      "blah" -> JsString(i.blah)
    ))
  }
}

来源:documentation 解释了如何进行读写操作,而我在这里所做的是将这两者结合起来形成格式。

【讨论】:

  • 哇,这太棒了!您的代码并不完全正确,例如,您必须将读取函数的右侧包装在 JsSuccess 中,但总的来说它可以工作。杰出的。我真正需要的是第一行。不将 Format 定义为 val,而是将其定义为具有类型参数的函数。接下来我将完成读取函数的最终确定(使用.validate而不是.as添加检查),然后我将分享最终代码以供进一步参考。
猜你喜欢
  • 1970-01-01
  • 2011-09-12
  • 1970-01-01
  • 2020-06-16
  • 2020-12-10
  • 2017-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多