如何迭代 Scala 中 Future List 的结果?
How to iterate over result of Future List in Scala?
我是 Scala 的新手,正在尝试 akka
。我正在尝试从 Scala 中的 MongoDB 访问数据,并希望将其转换为 JSON 和 XML 格式。
下面附加的代码使用路径 /getJson 并调用 getJson() 函数以 future.
的形式获取数据
get {
concat(
path("getJson"){
val f = Patterns.ask(actor1,getJson(),10.seconds)
val res = Await.result(f,10.seconds)
val result = res.toString
complete(res.toString)
}
}
getJson()方法如下:
def getJson()= {
val future = collection.find().toFuture()
future
}
我在文件 Greeting.scala
中有一个问候案例 class:
case class Greeting(msg:String,name:String)
和MyJsonProtocol.scala用于将scala对象编组为JSON格式的文件如下:
trait MyJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol {
implicit val templateFormat = jsonFormat2(Greeting)
}
我在 Postman 中得到 complete(res.toString)
的输出:
Future(Success(List(
Iterable(
(_id,BsonObjectId{value=5fc73944986ced2b9c2527c4}),
(msg,BsonString{value='Hiiiiii'}),
(name,BsonString{value='Ruchirrrr'})
),
Iterable(
(_id,BsonObjectId{value=5fc73c35050ec6430ec4b211}),
(msg,BsonString{value='Holaaa Amigo'}),
(name,BsonString{value='Pablo'})),
Iterable(
(_id,BsonObjectId{value=5fc8c224e529b228916da59d}),
(msg,BsonString{value='Demo'}),
(name,BsonString{value='RuchirD'}))
)))
有人可以告诉我如何迭代此输出并以 JSON 格式显示它吗?
首先,不要在complete(res.toString)
中调用toString
。
如 AkkaHTTP json support guide 中所述,如果您设置正确,您的案例 class 将自动转换为 json。
但是正如我在输出中看到的,您的 res
不是 Greeting
类型的对象。看起来它与 Greeting
有某种关系并且具有相同的结构。似乎是 MongoDB 请求的原始输出。如果这是一个正确的假设,您应该将原始输出从 MongoDB 转换为您的 Greeting
案例 class。
我想这可以在 collection.find()
.
之后的 getJson()
完成
使用 Scala 时,了解类型非常重要。第一步是至少了解变量和值的类型。
如果你看看这个方法,
def getJson() = {
val future = collection.find().toFuture()
future
}
缺少所有级别的类型类型信息,这是一个非常糟糕的做法。
我假设您正在使用 mongo-scala-driver。而你的 collection
实际上是 MongoCollection[Document]
.
这意味着 collection.find()
的输出应该是 FindOberservable[Document]
,因此 collection.find().toFuture()
应该是 Future[Seq[Document]]
。所以,你的 getJson
方法应该写成,
def getJson(): Future[Seq[Document]] =
collection.find().toFuture()
现在,这意味着您要将 Future[Seq[Document]]
传递给 actor1
,这又是一种不好的做法。你永远不应该在演员之间发送任何类型的 Future
值。看起来你的 actor1
什么也没做,只是发回了同样的信息。为什么这个 actor1
什么都不做时甚至需要它?
这意味着您的 f
是 Future[Future[Seq[Document]]]
。那么你正在使用 Await.result
来获得这个未来的结果 f
。这又是一个反模式,因为 Await
阻塞了你的线程。
现在,您的 res
是 Future[Seq[Document]]
。您正在将其转换为 String
并使用 complete
.
发回该字符串
你的 JsonProtocol
没有工作,因为你甚至没有传递任何东西 Greeting's
。
您必须执行以下操作,
- 从 mongo 中读取原始 Bson 对象。
- 将原始 Bson 对象转换为您的
Gretting
对象。
- 使用这些
Gretting
对象完成您的结果。 JsonProtocol 应该考虑将这些 Greeting
对象转换为 Json
.
完成所有这些操作的最简单方法是使用 mongo 驱动程序的 CodecRegistreis
。
case class Greeting(msg:String, name:String)
现在,您的 MongoDAL
对象将如下所示(它可能缺少一些导入,请像您在自己的代码中那样填充任何缺少的导入)。
import org.mongodb.scala.bson.codecs.Macros
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.bson.codecs.configuration.CodecRegistries
import org.mongodb.scala.{MongoClient, MongoCollection, MongoDatabase}
object MongoDAL {
val greetingCodecProvider = Macros.createCodecProvider[Greeting]()
val codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromProviders(greetingCodecProvider),
DEFAULT_CODEC_REGISTRY
)
val mongoClient: MongoClient = ... // however you are connecting to mongo and creating a mongo client
val mongoDatabase: MongoDatabase =
mongoClient
.getDatabase("database_name")
.withCodecRegistry(codecRegistry)
val greetingCollection: MongoCollection[Greeting] =
mongoDatabase.getCollection[Greeting]("greeting_collection_name")
def fetchAllGreetings(): Future[Seq[Greeting]] =
greetingCollection.find().toFuture()
}
现在,您的路线可以定义为
get {
concat(
path("getJson") {
val greetingSeqFuture: Future[Seq[Greeting]] = MongoDAL.fetchAllGreetings()
// I don't see any need for that actor thing,
// but if you really need to do that, then you can
// do that by using flatMap to chain future computations.
val actorResponseFuture: Future[Seq[Greeting]] =
greetingSeqFuture
.flatMap(greetingSeq => Patterns.ask(actor1, greetingSeq, 10.seconds))
// complete can handle futures just fine
// it will wait for futre completion
// then convert the seq of Greetings to Json using your JsonProtocol
complete(actorResponseFuture)
}
}
我是 Scala 的新手,正在尝试 akka
。我正在尝试从 Scala 中的 MongoDB 访问数据,并希望将其转换为 JSON 和 XML 格式。
下面附加的代码使用路径 /getJson 并调用 getJson() 函数以 future.
get {
concat(
path("getJson"){
val f = Patterns.ask(actor1,getJson(),10.seconds)
val res = Await.result(f,10.seconds)
val result = res.toString
complete(res.toString)
}
}
getJson()方法如下:
def getJson()= {
val future = collection.find().toFuture()
future
}
我在文件 Greeting.scala
中有一个问候案例 class:
case class Greeting(msg:String,name:String)
和MyJsonProtocol.scala用于将scala对象编组为JSON格式的文件如下:
trait MyJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol {
implicit val templateFormat = jsonFormat2(Greeting)
}
我在 Postman 中得到 complete(res.toString)
的输出:
Future(Success(List(
Iterable(
(_id,BsonObjectId{value=5fc73944986ced2b9c2527c4}),
(msg,BsonString{value='Hiiiiii'}),
(name,BsonString{value='Ruchirrrr'})
),
Iterable(
(_id,BsonObjectId{value=5fc73c35050ec6430ec4b211}),
(msg,BsonString{value='Holaaa Amigo'}),
(name,BsonString{value='Pablo'})),
Iterable(
(_id,BsonObjectId{value=5fc8c224e529b228916da59d}),
(msg,BsonString{value='Demo'}),
(name,BsonString{value='RuchirD'}))
)))
有人可以告诉我如何迭代此输出并以 JSON 格式显示它吗?
首先,不要在complete(res.toString)
中调用toString
。
如 AkkaHTTP json support guide 中所述,如果您设置正确,您的案例 class 将自动转换为 json。
但是正如我在输出中看到的,您的 res
不是 Greeting
类型的对象。看起来它与 Greeting
有某种关系并且具有相同的结构。似乎是 MongoDB 请求的原始输出。如果这是一个正确的假设,您应该将原始输出从 MongoDB 转换为您的 Greeting
案例 class。
我想这可以在 collection.find()
.
getJson()
完成
使用 Scala 时,了解类型非常重要。第一步是至少了解变量和值的类型。
如果你看看这个方法,
def getJson() = {
val future = collection.find().toFuture()
future
}
缺少所有级别的类型类型信息,这是一个非常糟糕的做法。
我假设您正在使用 mongo-scala-driver。而你的 collection
实际上是 MongoCollection[Document]
.
这意味着 collection.find()
的输出应该是 FindOberservable[Document]
,因此 collection.find().toFuture()
应该是 Future[Seq[Document]]
。所以,你的 getJson
方法应该写成,
def getJson(): Future[Seq[Document]] =
collection.find().toFuture()
现在,这意味着您要将 Future[Seq[Document]]
传递给 actor1
,这又是一种不好的做法。你永远不应该在演员之间发送任何类型的 Future
值。看起来你的 actor1
什么也没做,只是发回了同样的信息。为什么这个 actor1
什么都不做时甚至需要它?
这意味着您的 f
是 Future[Future[Seq[Document]]]
。那么你正在使用 Await.result
来获得这个未来的结果 f
。这又是一个反模式,因为 Await
阻塞了你的线程。
现在,您的 res
是 Future[Seq[Document]]
。您正在将其转换为 String
并使用 complete
.
你的 JsonProtocol
没有工作,因为你甚至没有传递任何东西 Greeting's
。
您必须执行以下操作,
- 从 mongo 中读取原始 Bson 对象。
- 将原始 Bson 对象转换为您的
Gretting
对象。 - 使用这些
Gretting
对象完成您的结果。 JsonProtocol 应该考虑将这些Greeting
对象转换为Json
.
完成所有这些操作的最简单方法是使用 mongo 驱动程序的 CodecRegistreis
。
case class Greeting(msg:String, name:String)
现在,您的 MongoDAL
对象将如下所示(它可能缺少一些导入,请像您在自己的代码中那样填充任何缺少的导入)。
import org.mongodb.scala.bson.codecs.Macros
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.bson.codecs.configuration.CodecRegistries
import org.mongodb.scala.{MongoClient, MongoCollection, MongoDatabase}
object MongoDAL {
val greetingCodecProvider = Macros.createCodecProvider[Greeting]()
val codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromProviders(greetingCodecProvider),
DEFAULT_CODEC_REGISTRY
)
val mongoClient: MongoClient = ... // however you are connecting to mongo and creating a mongo client
val mongoDatabase: MongoDatabase =
mongoClient
.getDatabase("database_name")
.withCodecRegistry(codecRegistry)
val greetingCollection: MongoCollection[Greeting] =
mongoDatabase.getCollection[Greeting]("greeting_collection_name")
def fetchAllGreetings(): Future[Seq[Greeting]] =
greetingCollection.find().toFuture()
}
现在,您的路线可以定义为
get {
concat(
path("getJson") {
val greetingSeqFuture: Future[Seq[Greeting]] = MongoDAL.fetchAllGreetings()
// I don't see any need for that actor thing,
// but if you really need to do that, then you can
// do that by using flatMap to chain future computations.
val actorResponseFuture: Future[Seq[Greeting]] =
greetingSeqFuture
.flatMap(greetingSeq => Patterns.ask(actor1, greetingSeq, 10.seconds))
// complete can handle futures just fine
// it will wait for futre completion
// then convert the seq of Greetings to Json using your JsonProtocol
complete(actorResponseFuture)
}
}