我正在尝试为json建模消息传递。 这是json的例子:
// message1
{
"messageType" : "JoinChannelCommand",
"content" : {
"channelId" : "abc"
}
}
// message 2
{
"messageType" : "SyncCommand",
"content" : {
"updatedAfter" : 1516726583
}
}
下面是代表json
的类case class JoinChannelCommand(channelId: String)
object JoinChannelCommand {
def unapply(js: JsValue) = {
(js \ "messageType").asOpt[String].flatMap { messageType =>
val className = this.getClass.getSimpleName.stripSuffix("$")
if (messageType == className) {
(js \ "content" \ "channelId").asOpt[String]
} else {
None
}
}
}
case class SyncCommand(updateAfter: Timestamp)
object SyncCommand {
def unapply(js: JsValue) = {
(js \ "messageType").asOpt[String].flatMap { messageType =>
val className = this.getClass.getSimpleName.stripSuffix("$")
if (messageType == className) {
(js \ "content" \ "updatedAfter").asOpt[Long].map(new Timestamp(_))
} else {
None
}
}
}
}
这样,我就可以用这种方式进行模式匹配
x matchs {
case JoinChannelCommand(channelId) =>
case SyncCommand(updatedAfter) =>
}
如您所见,有很多样板代码。 我想知道是否有办法在运行时检查字段(可能像反射),然后构建一个在unapply函数中使用的元组。
然后我只需要写:
trait MagicCommand {
def unapply(js: JsValue) = { // <- where magic happened
}
}
case class JoinChannelCommand(channelId: String)
object JoinChannelCommand extends MagicCommand
当命令量变大时会更加方便。
答案 0 :(得分:0)
使用uPickle,请参阅CustomKeys部分中的最后一个示例here。基本上,它在编码时在JSON对象中添加$type
字段,并使用该字段映射到适当的案例类。
或者,如果您将命令分布在特定的REST请求处理程序上(并且可选地消除messageType
和content
封装),使用play json validate
宏的验证会变得更容易。
;
case class JoinChannelCommand(channelId: String)
object JoinChannelCommand {
implicit lazy val joinChannelCommandFormat = Json.format[JoinChannelFormat]
}
假设您正在POST到以下控制器方法
def joinChannelHandler = Action.async(parse.default) { implicit request =>
val joinChannelCommandOpt: Option[JoinChannelCommand] = request.body.asJson.get.validate[JoinChannelCommand].asOpt
joinChannelCommandOpt match {
case None => ....
case Some(c: JoinChannelCommand) => ....
}
...
}
它看起来很精简。全自动。如果您的案例类包含Option
字段,则只要可选字段值为None
,结果编码的json就不会具有这些字段。这与JS处理JSON的方式非常相似。
希望这有帮助。