4

我是 Scala 的新手,正在探索隐式转换和著名的蛋糕模式的可能性。我尝试创建将 id 值列为抽象类型的模型类,以避免泄漏实现细节。我还将它混合到蛋糕图案特征包装中。一切正常,除了从 id 到 JSON 的隐式转换(在 Play 框架内)。无论我做什么,Scala 编译器都找不到隐式转换。

这是重现该问题的代码:

import anorm._
import play.api.libs.json._

trait Card {
  type Key
  val NoId: Key
  val id: Key
  val name: String
}

trait CardModelComponent {
  val cardModel: CardModel
  trait CardModel {
    def findById(id: Long): Option[Card]
    def findAll: Seq[Card]
    def delete(id: Long)
    def create(name: String): Option[Card]
  }
}

trait CardModelComponentImpl extends CardModelComponent {
  case class CardImpl(id: Pk[Long], name: String) extends Card {
    type Key = Pk[Long]

    object Key extends Writes[Key] {
      implicit def writes(key: Key): JsValue = {
        key match {
          case Id(idVal: Long) => JsNumber(idVal)
          case _ => JsNull
        }
      }
    }
    val NoId = NotAssigned
  }

  class CardModelImpl extends CardModel {
    def findById(id: Long): Option[Card] = { None }
    def findAll: Seq[Card] = { Seq(CardImpl(Id(1), "Some card"))}
    def delete(id: Long) {}
    def create(name: String): Option[Card] = { Some(CardImpl(Id(1), name)) }
  }
}

object ComponentsRegistry extends
CardModelComponentImpl {

  val cardModel = new CardModelImpl
}

val card = ComponentsRegistry.cardModel.create("Test card").get
Json.toJson(card.id)

我得到的错误输出如下:

> card: Card = CardImpl(1,Test card)
> <console>:19: error: No Json deserializer found for type card.Key. Try to implem
  ent an implicit Writes or Format for this type.
                Json.toJson(card.id)
                           ^

有什么办法让它工作吗?看起来像蛋糕模式包装从编译器中隐藏了太多的类型信息,正如我从card.Key类型名称中猜测的那样。

我还尝试直接为 Pk 创建 Writer 实现,结果与错误相同。

4

3 回答 3

2

Writes[Key]应该是隐式的实例,而不是writes()成员方法。隐式实例也必须在toJson()调用方法的范围内。

于 2013-01-14T10:22:11.440 回答
1

就像 Jesper Nordenberg 所写的那样,您必须将 Writes[Key] 的实例纳入范围。一种方法是要求 Card 的实现发布 Writer 实例,然后将其导入到您调用 toJson 的任何位置,如下所示:

...

trait Card {
  type Key

  val NoId: Key
  val id: Key
  val name: String
  implicit val Key: Writes[Key]
}

...

trait CardModelComponentImpl extends CardModelComponent {
  case class CardImpl(id: Pk[Long], name: String) extends Card {
    type Key = Pk[Long]

    implicit object Key extends Writes[Key] {
      def writes(key: Key): JsValue = {
        key match {
          case Id(idVal: Long) => JsNumber(idVal)
          case _ => JsNull
        }
      }
    }

    val NoId = NotAssigned
  }

  class CardModelImpl extends CardModel {
    def findById(id: Long): Option[Card] = { None }
    def findAll: Seq[Card] = { Seq(CardImpl(Id(1), "Some card"))}
    def delete(id: Long) {}
    def create(name: String): Option[Card] = { Some(CardImpl(Id(1), name)) }
  }
}

...

val card = ComponentsRegistry.cardModel.create("Test card").get
import card.Key
Json.toJson(card.id)

或明确传递:

val card = ComponentsRegistry.cardModel.create("Test card").get
Json.toJson(card.id)(card.Key)
于 2013-01-14T10:43:43.370 回答
1

我稍微修改了你的代码。我希望这对你有帮助。

package models
import anorm._
import play.api.libs.json._

trait Card[T] {
  val NoId: T
  val id: T
  val name: String
}

trait CardModelComponent[T] {
  val cardModel: CardModel
  trait CardModel {
    def findById(id: Long): Option[Card[T]]
    def findAll: Seq[Card[T]]
    def delete(id: Long)
    def create(name: String): Option[Card[T]]
  }
}

trait CardModelComponentImpl extends CardModelComponent[Pk[Long]] {
  type Key = Pk[Long]
  case class CardImpl(id: Key, name: String) extends Card[Key] {
    val NoId = NotAssigned
  }


  class CardModelImpl extends CardModel {
    def findById(id: Long): Option[Card[Key]] = { None }
    def findAll: Seq[Card[Key]] = { Seq(CardImpl(Id(1), "Some card"))}
    def delete(id: Long) {}
    def create(name: String): Option[Card[Key]] = { Some(CardImpl(Id(1), name)) }
  }
}

object ComponentsRegistry extends CardModelComponentImpl {
  val cardModel = new CardModelImpl
  implicit object KeyWrites extends Writes[Key] {
    def writes(key: Key): JsValue = {
      key match {
        case Id(idVal: Long) => JsNumber(idVal)
        case _ => JsNull
      }
    }
  }
}

然后你可以像这样使用:

import models.ComponentsRegistry
import models.ComponentsRegistry.KeyWrites
val card = ComponentsRegistry.cardModel.create("Test card").get
Json.toJson(card.id)
于 2013-01-14T10:58:00.037 回答