Scala Akka HTTP Actor 类协议类型不匹配错误

问题描述 投票:0回答:1

我正在使用 scala 3.3 和 Akka actor 构建 Akka HTTP 应用程序。

当在

star, planet or moon
对象中创建
StarRegistry
时,我希望将其值临时存储在
registry
函数的映射变量
map: Map[String, Set[HeavenlyBody]]
中。

map
应存储如下所示的值,每个参数都有其自己的类型,如下所示:


"stars" -> Set[CreateStar], 
"planets" -> Set[CreatePlanet], 
"moons" -> Set[CreateMoon], 

starRoutes
脚本中是 API。

stars
API 路由用于创建新的星星,并获取上面存储
map
中存储的所有星星。

当获取

stars
GET API 路由时,我希望 api 从上面的
stars
函数的
stars
中获取存储在
registry
字段中的所有
map

star
功能的描述与
moon
planet
功能的描述类似。

当前代码给出错误:


Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:
replyTo ! Stars(map.get("stars").toSeq)
Found:    Seq[Set[com.example.HeavenlyBody]]
Required: Seq[com.example.CreateStar]

Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:199:45 
replyTo ! Planets(map.get("planets").toSeq)
Found:    Seq[Set[com.example.HeavenlyBody]]
Required: Seq[com.example.CreatePlanet]

Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:213:41 
replyTo ! Moons(map.get("moons").toSeq)
Found:    Seq[com.example.HeavenlyBody]
Required: Seq[com.example.CreateMoon]

我当前的代码如下所示。

预先感谢您的帮助!

StarRegistry.scala:


sealed trait HeavenlyBody

final case class CreateStar(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody
final case class CreatePlanet(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody
final case class CreateMoon(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody

final case class Stars(stars: immutable.Seq[CreateStar])
final case class Planets(planets: immutable.Seq[CreatePlanet]) 
final case class Moons(moons: immutable.Seq[CreateMoon]) 

final case class Size(radius: Long)

object StarRegistry {

  // actor protocol
  sealed trait Command
  final case class GetStars(replyTo: ActorRef[Stars]) extends Command
  final case class NewStar(star: CreateStar, replyTo: ActorRef[ActionPerformed]) extends Command

  final case class GetPlanets(replyTo: ActorRef[Planets]) extends Command
  final case class NewPlanet(planet: CreatePlanet, replyTo: ActorRef[ActionPerformed]) extends Command

  final case class GetMoons(replyTo: ActorRef[Moons]) extends Command
  final case class NewMoon(moon: CreateMoon, replyTo: ActorRef[ActionPerformed]) extends Command

  final case class ActionPerformed(description: String)

  def apply(): Behavior[Command] = registry(Map("stars" -> Set.empty, "planets" -> Set.empty, "moons" -> Set.empty))

  private def registry(map: Map[String, Set[HeavenlyBody]]): Behavior[Command] =
    Behaviors.receiveMessage {

      case GetStars(replyTo) =>
        replyTo ! Stars(map.get("stars").toSeq)
        Behaviors.same
      case NewStar(star, replyTo) =>
        replyTo ! ActionPerformed(s"Star '${star.name}' created!")
        registry(map("stars") += star)

      case GetPlanets(replyTo) =>
        replyTo ! Planets(map.get("planets").toSeq)
        Behaviors.same
      case NewPlanet(planet, replyTo) =>
        replyTo ! ActionPerformed(s"Planet '${planet.name}' created!")
        registry(map("planets") += planet)

      case GetMoons(replyTo) =>
        replyTo ! Moons(map.get("moons").toSeq)
        Behaviors.same
      case NewMoon(moon, replyTo) =>
        replyTo ! ActionPerformed(s"Moon '${moon.name}' created!")
        registry(map("moons") += moon)

    }
}

jsonFormats.scala:


...
...

object JsonFormats  {
  import DefaultJsonProtocol._

  implicit val sizeJsonFormat: RootJsonFormat[Size] = jsonFormat1(Size.apply)
  implicit val createStarJsonFormat: RootJsonFormat[CreateStar] = jsonFormat4(CreateStar.apply)
  implicit val createPlanetJsonFormat: RootJsonFormat[CreatePlanet] = jsonFormat4(CreatePlanet.apply)
  implicit val createMoonJsonFormat: RootJsonFormat[CreateMoon] = jsonFormat4(CreateMoon.apply)
  implicit val starsJsonFormat: RootJsonFormat[Stars] = jsonFormat1(Stars.apply)
  implicit val planetsJsonFormat: RootJsonFormat[Planets] = jsonFormat1(Planets.apply)
  implicit val moonsJsonFormat: RootJsonFormat[Moons] = jsonFormat1(Moons.apply) 
  implicit val actionPerformedJsonFormat: RootJsonFormat[ActionPerformed]  = jsonFormat1(ActionPerformed.apply)
}

starRoutes.scala:


class StarRoutes(starRegistry: ActorRef[StarRegistry.Command])(implicit val system: ActorSystem[_]) {

  import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
  import JsonFormats._ //#import-json-formats

  def getStars(): Future[Stars] =
    starRegistry.ask(GetStars.apply)
  def createStar(star: CreateStar): Future[ActionPerformed] =
    starRegistry.ask(NewStar(star, _))

  def getPlanets(): Future[Planets] =
    starRegistry.ask(GetPlanets.apply)
  def createPlanet(planet: CreatePlanet): Future[ActionPerformed] =
    starRegistry.ask(NewPlanet(planet, _))

  def getMoons(): Future[Moons] =
    starRegistry.ask(GetMoons.apply)
  def getMoon(name: String): Future[GetMoonResponse] =
    starRegistry.ask(GetMoon(name, _))

  val starRoutes: Route =
    ...
    pathPrefix("stars") {
      concat(
        pathEnd {
          concat(
            get {
              complete(getStars())
            },
            post {
              entity(as[CreateStar]) { star =>
                onSuccess(createStar(star)) { performed =>
                  complete((StatusCodes.Created, performed))
                }
              }
            })
        },
    }
    ...
    pathPrefix("planets") {
      concat(
        pathEnd {
          concat(
            get {
              complete(getPlanets())
            },
            post {
              entity(as[CreatePlanet]) { planet =>
                onSuccess(createPlanet(planet)) { performed =>
                  complete((StatusCodes.Created, performed))
                }
              }
            })
    },
    ...
    pathPrefix("moons") {
      concat(
        pathEnd {
          concat(
            get {
              complete(getMoons())
            },
            post {
              entity(as[CreateMoon]) { moon =>
                onSuccess(createMoon(moon)) { performed =>
                  complete((StatusCodes.Created, performed))
                }
              }
            })
    }

scala akka akka-stream akka-http
1个回答
0
投票
private def registry(map: Map[String, Set[HeavenlyBody]]): Behavior[Command] =
  Behaviors.receiveMessage {
    case GetStars(replyTo) =>
      replyTo ! Stars(map.get("stars").toSeq)

您收到错误是因为

map.get(...).toSeq
返回
Seq[HeavenlyBody]
而您想要
Seq[CreateStar]
。您可能认为
Seq
的所有元素都是
CreateStar
,但编译器无法判断。

解决方案是使用

collect
来约束类型:

replyTo ! Stars(map.get("stars").toSeq.collect{x: CreateStar => x})

这将丢弃所有不是

CreateStar
的元素并根据需要返回
Seq[CreateStar]

您可能还想考虑使用

enum HeavenlyBody
而不是密封特征。

© www.soinside.com 2019 - 2024. All rights reserved.