Scala Play - How to Format Generics for JSON Conversion

I am learning more and more about Scala and what a glorious playframework is. But there are some things that bother me and that I cannot work.

I like to use Generics for some collections, for example. But I need the ones that will be stored in our database, in JSON. There is such a cool thing for automatic converting, but it doesn't work for generics, I have not tried it in any way: - /

Ok, to be specific, first the code:

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]
}

      

An item is a basic abstract class of various items that can be placed in this inventory. It does not matter. But sometimes I want to have an inventory that just works for ItemType A, lets call it AItem

. So I want to create my inventory with something like this: val myInventory = Inventory[AItem]("content of vector here")

and when I call myInventory.item(2)

then I want to get the item in slot 2 and it should be a type object AItem

, not just Item

. (This is why I am using generics here)

So the problem is

The implicit format for Inventory

doesn't work, obviously. Item

does, also with all the special elements, I can post the code for it below and InventorySlot

should work too.

Compilation error:

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]
                                  ^

      

I tried to write read and write explicitly like

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

      

which doesn't even work in my IDE and I can't seem to find the problem. I'm confused. I don't know where my mistake is, or if I am doing something wrong, or just missing something.

Any help would be appreciated.

I am so helpless that I even thought about making a class for all possible types of inventory, for example

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

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

      

which is working. No problem, it's okay. So ... I don't understand. Why does this work if it seems exactly the same, just hardcoded?

application

The formatter element that works:

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)
  }
}

      

+3


source to share


1 answer


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)
    ))
  }
}

      



Source: documentation explains how to do this for read and write, and what I've done here is combine the two for format.

+3


source







All Articles