我有一个演员从播放2.3中的websocket接收JsValue。我还有一个定义Reads转换器的case类。当我尝试对案例类进行模式匹配时,它总是匹配JsValue而不是案例类。
case class Ack(messageType: String, messageId: Int){
implicit val ackReads: Reads[Ack] = (
(JsPath \ "message_type").read[String] and
(JsPath \ "message_id").read[Int]
)(Ack.apply _)
}
class ChannelActor(out: ActorRef) extends Actor{
def receive = {
case a: Ack =>
println(s"Acknowledged! $a")
case msg: JsValue =>
println("Got other jsvalue")
case _ =>
println("Got something else")
}
}
我如何模式匹配我从websocket接收的JsValue与案例类中的Reads验证器?
编辑:我找到了一种解决方法,通过手动模式匹配JsValue来确定我需要验证的类型。代码现在看起来像这样:
case class Ack(messageType: String, messageId: Int)
object Ack{
implicit val ackReads: Reads[Ack] = (
(JsPath \ "message_type").read[String](verifying[String](_ == "ack")) and
(JsPath \ "message_id").read[Int]
)(Ack.apply _)
implicit val ackWrites: Writes[Ack] = (
(JsPath \ "message_type").write[String] and
(JsPath \ "message_id").write[Int]
)(unlift(Ack.unapply))
}
class ChannelActor(out: ActorRef) extends Actor{
def receive = {
case msg: JsValue =>
(msg \ "message_type").asOpt[String] match {
case Some("ack") =>
msg.validate[Ack] match{
case ack: JsSuccess[Ack] => println("got valid ack message")
case e: JsError => out ! Json.obj("error" -> s"invalid format for ack message ${JsError.toFlatJson(e).toString()}")
}
case None => out ! Json.obj("error" -> "you must send a message_type with your json object")
case t => out ! Json.obj("error" -> s"unknown message type ${t.get}")
}
case _ => out ! Json.obj("error" -> "unknown message format")
}
}
这实现了我想要的但我觉得它不是在Play中验证JSON消息的“正确”或最优雅的解决方案,并且在我实现更多消息类型时会很麻烦。
答案 0 :(得分:0)
请参阅http://www.playframework.com/documentation/2.3.x/ScalaWebSockets
它在文档中逐字记载
import play.api.libs.json._
implicit val inEventFormat = Json.format[InEvent]
implicit val outEventFormat = Json.format[OutEvent]
import play.api.mvc.WebSocket.FrameFormatter
implicit val inEventFrameFormatter = FrameFormatter.jsonFrame[InEvent]
implicit val outEventFrameFormatter = FrameFormatter.jsonFrame[OutEvent]
import play.api.mvc._
import play.api.Play.current
def socket = WebSocket.acceptWithActor[InEvent, OutEvent] { request => out =>
MyWebSocketActor.props(out)
}
编辑:好的,你找到了。问题是Json.format写入方法不提供有关类型的信息。让我深入了解一下。
例如
case class Test(a: String)
implicit val f = Json.format[Test]
f.writes(Test1("hey")) // >> {"a":"hey"} note nothing says it's a Test1 instances
答案 1 :(得分:0)
通过以下方式创建这样的匹配器并不困难:
case class ReadsMatch[T](reads: Reads[T]) {
def unapply(js: JsValue) = reads.reads(js).asOpt
}
然后您可以在代码中使用它,如下所示:
class ChannelActor(out: ActorRef) extends Actor{
// create matcher instance
val ackJson = ReadsMatch[Ack]
def receive = {
// unapply ackJson in the pattern matching case
case ackJson(Ack(messageType, messageId)) =>
??? // do something with extracted messageType and messageId
case _ => out ! Json.obj("error" -> "unknown message format")
}
}