如何从 akka singleRequest 响应正文中自定义解组

问题描述

我从带有自定义主体的 singleRequest 中使用了一些数据,并且我尝试使用 Unmarshall 将其序列化为自定义 case 类,但它不起作用;身体反应是这样的:

{
    "teams": [
        {
            "id": "21312","name": "team1","color": "#4169E1","avatar": null,"members": [
                {
                    "user": {
                        "id": 12737636,"username": "user2","email": "user2@email.com","color": "#827718","profilePicture": null,"initials": "U2","role": 4,"custom_role": null,"last_active": "1613419183004","date_joined": "1612542647748","date_invited": "1612542548880"
                    },"invited_by": {
                        "id": 68732,"username": "user1","color": "#f8306d","email": "user1@email.com","initials": "U1","profilePicture": "https://attachments.clickup.com/profilePictures/image.jpg"
                    },"can_see_time_spent": true,"can_see_time_estimated": true,"can_see_points_estimated": true,"can_edit_tags": false
                },{
                    "user": {
                        "id": 12715129,"username": "user3","email": "user3@email.com","color": "#04a9f4","initials": "IM","last_active": "1613483780281","date_joined": "1611598611255","date_invited": "1611597360856"
                    },"invited_by": {
                        "id": 4371505,"username": "user34","email": "user34@email.com","initials": "U34","can_edit_tags": false
                }
            ]
        }
    ]
}

获取数据的代码

val responseFuture = Http()
      .singleRequest(HttpRequest( method = GET,uri = "https://apiRest/api/v2/team" )
        .withHeaders(RawHeader("authorization","xxxxxxxxxxxx")))

我有这个案例类来序列化它:

    case class Invited_By (id: Long,username:Option[String],color : Option[String],email: Option[String],initials : Option[String],profilePicture: Option[String])
  case class User ( id: Long,username: Option[String],color: Option[String],profilePicture: Option[String],initials: Option[String],role: Option[Int],custom_role: Option[String],last_active: Option[String],date_joined: Option[String],date_invited: Option[String] )
  case class Member (user: User,invited_By: Invited_By,can_see_time_spent: Boolean,can_see_time_estimated:Boolean,can_see_points_estimated:Boolean,can_edit_tags:Boolean)
  case class Team (id : String,name: Option[String],avatar: Option[String],members: Seq[Member] )
  case class Teams( teams: Seq[Team] )

我试过这个;这不起作用:

import play.api.libs.json._
import play.api.libs.functional.Syntax._

implicit val TeamsReads: Reads[Teams] = Json.reads[Teams]

    val responseFuture = Http()
      .singleRequest(HttpRequest( method = GET,"XXXXXXXXXXXX")))
      .flatMap{
        res =>
          Unmarshal(res).to[String]
            .map {
              data =>
                val jsValue:JsValue = Json.parse(data)
                val result: JsResult[Teams] = jsValue.validate[Teams]
                result
            }
        }

有更好的方法吗??

注意:我发现 akka-http-spray-json ,但我不知道如何实现它

解决方法

为了使用 akka-http-spray-json,您需要导入这些库。

"com.typesafe.akka" %% "akka-http" % akkaHttpVersion,  "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion,  "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion,  "com.typesafe.akka" %% "akka-http2-support" % akkaHttpVersion,

然后按照以下步骤进行编组和解组 JSON 字符串。

  • 步骤 1 - 导入喷雾 json import spray.json._
  • 第 2 步 - 创建 JSON 协议。 extends DefaultJsonProtocol 的特征。请注意,Chat 类有 4 个参数,因此我在协议中使用了 jsonFormat4
  • 第 3 步 - 扩展您创建的特征
  • 第 4 步 - 添加 sprayJsonSupport

每次我需要向 akka 添加 json 支持时,我都会参考这个小例子:

import akka.actor.{Actor,ActorLogging,ActorSystem,Props}
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.server.Directives._
import akka.pattern.ask
import akka.util.Timeout
import spray.json._ // step 1 - import spray json
import scala.concurrent.Future
import scala.concurrent.duration._

// case class Player(nickname: String,characterClass: String,level: Int)
case class Chat(sender: String,receiver: String,message: String,groupChatName: String)

case object GetAllUsersInChat

case class AddUsers(player: Chat)

// step 2 - the JSON protocol
trait ChatJsonProtocol extends DefaultJsonProtocol {
  implicit val chatFormat = jsonFormat4(Chat)
}

class ChatActor extends Actor with ActorLogging {
  var users = Map[String,Chat]()

  override def receive: Receive = {
    case GetAllUsersInChat =>
      log.info(s"getting all users")
      sender() ! users.values.toList
    case AddUsers(user) =>
      log.info(s"trying to add user $user")
      users = users + (user.sender -> user)
    case _ => log.info(s"unknown message")
  }
}

// step 3 - extend ChatJsonProtocol
// step 4 - add sprayJsonSupport
object ChatMarshallingJSON extends ChatJsonProtocol with SprayJsonSupport {
  implicit val system = ActorSystem("ChatMarshallingJSON")
  val chatActor = system.actorOf(Props[ChatActor],"chatActor")
  // boot strap some users
  val chatusers = List(
    Chat("sender1","receiver1","message1","groupChat1"),    Chat("sender2","receiver2","message2","groupChat2"),    Chat("sender3","receiver3","message3","groupChat3")
  )
  chatusers.foreach { user =>
    chatActor ! AddUsers(user)
  }

  implicit val defaultTimeout = Timeout(2 seconds)

  val chatRoutes = {
    path("api" / "chat") {
      get {
        // 3: get all users in the chat
        val allUsersInChatFuture: Future[List[Chat]] = (chatActor ? GetAllUsersInChat).mapTo[List[Chat]]
        complete(allUsersInChatFuture)
      }
    }
  }

  def main(args: Array[String]): Unit = {
    println("http GET localhost:8080/api/chat")
    Http()
      .newServerAt("localhost",8080)
      .bindFlow(chatRoutes)
  }
}