如何迭代 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 什么都不做时甚至需要它?

这意味着您的 fFuture[Future[Seq[Document]]]。那么你正在使用 Await.result 来获得这个未来的结果 f。这又是一个反模式,因为 Await 阻塞了你的线程。

现在,您的 resFuture[Seq[Document]]。您正在将其转换为 String 并使用 complete.

发回该字符串

你的 JsonProtocol 没有工作,因为你甚至没有传递任何东西 Greeting's

您必须执行以下操作,

  1. 从 mongo 中读取原始 Bson 对象。
  2. 将原始 Bson 对象转换为您的 Gretting 对象。
  3. 使用这些 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)
    }
}