如何使用Reads对JsValue进行模式匹配

fig*_*uts 6 scala playframework playframework-2.0

我有一个演员从播放2.3中的websocket接收JsValue.我还有一个定义Reads转换器的case类.当我尝试对案例类进行模式匹配时,它总是匹配JsValue而不是case类.

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")
  }
}
Run Code Online (Sandbox Code Playgroud)

我如何模式匹配我从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")
  }
}
Run Code Online (Sandbox Code Playgroud)

这实现了我想要的,但我觉得它不是在Play中验证JSON消息的"正确"或最优雅的解决方案,并且在我实现更多消息类型时会很麻烦.

Gui*_*ssé 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)
}
Run Code Online (Sandbox Code Playgroud)

编辑:好的,明白了。问题是 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
Run Code Online (Sandbox Code Playgroud)

  • 我不太明白。在这种情况下,“InEvent”和“OutEvent”类型是什么?我的 websocket 可能接受任意数量的不同格式的 json 消息。使用具有特定类型的acceptWithActor似乎会限制我的websocket仅接收一种格式的消息。您能否提供一个具体示例来说明如何使用 websocket 匹配多种格式/类型的 json 输入? (4认同)