我是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
作为List访问,因此预期的输出将沿着这些行:
{
"a":{
"a1":"val1",
"a2":"val2"
},
"b":[
{
"b1":"val 3",
"b2":"val 4"
},
{
"b1":"val 5",
"b2":"val 6"
},
{
"b1":"val 7",
"b2":"val 8"
}
]
}
感谢您的帮助。
答案 0 :(得分:0)
正如@cchantep在你的问题的评论中提到的那样,将Future
作为case class
声明的一部分是非常不寻常的 - 案例类非常适合封装不可变域对象(即不随着时间的推移而变化)但是一旦涉及Future[T]
,您可能会有多种结果:
Future
尚未完成Future
失败Future
已成功完成,并包含T
个实例您不希望将此时间内容与转换为JSON的行为纠缠在一起。因此,您应该在删除Future
的情况下对包装类进行建模:
case class AAndB(a: Option[A], b: Option[List[B]])
object AAndB {
implicit val implicitAAndBWrites = Json.writes[AAndB]
}
而是使用Scala / Play在Controller
类中对它们进行非常简洁的处理来访问每个内容。在下面的示例中,假设存在注入的服务类,如下所示:
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
- 理解来实现这种并行性)。如果{当yield
s成功完成时,for
- 理解的Future
块将仅执行 - 此时可以安全地访问内容。然后,构建包装类的实例,转换为JSON并将Ok
结果返回到Play是一个简单的问题。
请注意,yield
块的结果本身位于Future
内(在这种情况下它是Future[Result]
)所以我们使用Play的{{ 1}}动作构建器来处理这个问题 - 让Play处理所有实际的等待事件。