我是一个Scala新手,扩展了别人的代码。代码使用Play框架的JSON库。我正在访问Future[Option[A]]和Future[Option[List[B]]类的对象。类A和B都有自己的JSON writes方法,因此每个类都可以将JSON作为对web请求的响应返回。我正在尝试将这些组合到一个JSON响应中,我可以将其作为HTTP响应返回。
我认为创建一个将A和B组成单个类的类可以让我做到这一点,大致是这样的:
case class AAndB(a: Future[Option[A]], b: Future[Option[List[B]]])
object AAndB {
implicit val implicitAAndBWrites = Json.writes[AAndB]
}但这在所有地方都是失败的。A和B的结构都是这样的:
sealed trait A extends SuperClass {
val a1: String = "identifier"
}
case class SubA(a2: ClassA2) extends A {
override val a1: String = "sub identifier"
}
object SubA {
val writes = Writes[SubA] { aa =>
Json.obj(
"a1" -> aa.a1
"a2" -> aa.a2
)
}
}由于B是以列表的形式访问的,因此预期的输出将如下所示:
{
"a":{
"a1":"val1",
"a2":"val2"
},
"b":[
{
"b1":"val 3",
"b2":"val 4"
},
{
"b1":"val 5",
"b2":"val 6"
},
{
"b1":"val 7",
"b2":"val 8"
}
]
}非常感谢您的帮助。
发布于 2017-02-08 07:07:10
正如@cchantep在你的问题的评论中提到的,将Futures作为case class声明的一部分是非常不寻常的-- Future[T]类非常适合封装不可变域对象(也就是不随时间变化的域对象),但是一旦你涉及到case,你可能会有多个结果:
Future yet
Future failedFuture successfully,并包含T实例您不希望将这些时态内容与转换为JSON的操作混为一谈。出于这个原因,您应该对包装器类进行建模,并删除Future:
case class AAndB(a: Option[A], b: Option[List[B]])
object AAndB {
implicit val implicitAAndBWrites = Json.writes[AAndB]
}取而代之的是在你的Controller类中使用Scala/Play对它们的非常简洁的处理来访问它们的内容。在下面的示例中,假设存在如下注入的服务类:
class AService {
def findA(id:Int):Future[Option[A]] = ...
}
class BListService {
def findBs(id:Int):Option[Future[List[B]]] = ...
}下面是我们的控制器方法可能的样子:
def showCombinedJson(id:Int) = Action.async {
val fMaybeA = aService.findA(id)
val fMaybeBs = bService.findBs(id)
for {
maybeA <- fMaybeA
maybeBs <- fMaybeBs
} yield {
Ok(Json.toJson(AAndB(maybeA, maybeBs)))
}
}因此,我们在这里并行启动A查询和B查询(我们必须在for-comprehension之外执行此操作才能实现这种并行性)。只有当两个-comprehension都成功完成时,才会执行forFuture的Future块-此时可以安全地访问其中的内容。然后,只需构建包装器类的一个实例,转换为JSON并返回Ok结果即可播放。
请注意,yield块的结果本身将在一个Future中(在本例中是一个Future[Result]),所以我们使用Play的Action.async操作构建器来处理这一点-让Play处理所有实际等待发生的事情。
https://stackoverflow.com/questions/42100180
复制相似问题