如何从Scala中的Http响应实体解组自定义对象?

问题描述

我正在尝试通过Scala中的akka​​库从本地服务器检索一些数据。 数据以JSON格式从服务器返回,但是我无法以自定义类型将其解组。

自定义类是个人资料,其中包含个人资料列表。

  case class Profile(
    Name: String,Surname: String,Mail: String,Age: Int,Town: String,Role: String,PrimaryInstr: String,SecondaryInstr: String,PrimaryGenre: String,SecondaryGenre: String,Influences: String,RecordLabel: String,GigAvailability: String,RehearseAvailability: String,RecordingExperience: String,MusicalAge: Int)

  case class Profiles(profiles: Vector[Profile])

我尝试使用以下代码解组到“个人档案”,但是无法编译 由于错误

找不到参数um的隐式值:akka.http.scaladsl.unmarshalling.Unmarshaller [ResponseEntity,Profiles]

import akka.http.scaladsl.Http
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.client.RequestBuilding.Get
import akka.http.scaladsl.model.HttpResponse

... 

def getProfiles = {
        var req = Get("http://localhost:9090/profiles")
        val responseFuture: Future[HttpResponse] = Http().singleRequest(req)
        responseFuture
          .onComplete {
            case Success(response) =>
              println(response.entity)
              //Here I want actually Unmarshall to Profiles,not to String
              var responseAsstring = Unmarshal(response.entity).to[String] //Tried here with Profiles
              println(responseAsstring)
            case Failure(_)   => sys.error("something wrong")
          }
           ...
      }

使用[String]解组代码会产生此输出(缩写为“ ...”)。

httpentity.Strict(application / json,[{“ Name”:“ Amadeus”,“ Surname”:“ Rapisarda”,...,“ MusicalAge”:9},{“ Name”:“ Federico”,“姓“:” D'Ambrosio“,...,” MusicalAge“:24}]) FulfilledFuture([{“ Name”:“ Amadeus”,“ Surname”:“ Rapisarda”,...,“ MusicalAge”:9},{“ Name”:“ Federico”,“ Surname”:“ D'Ambrosio”, ...,“ MusicalAge”:24}])

如何获取个人档案对象? 预先感谢!

解决方法

我终于找到了行之有效的解决方案。

1-将这些依赖项添加到build.sbt文件中

val AkkaVersion = "2.6.9"
val AkkaHttpVersion = "10.2.0"
libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion,"com.typesafe.akka" %% "akka-stream" % AkkaVersion,"com.typesafe.akka" %% "akka-http" % AkkaHttpVersion,"com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion
)

2-在文件中添加这些导入

import akka.actor.typed.ActorSystem
import akka.actor.typed.scaladsl.Behaviors
import akka.http.scaladsl.Http
import akka.http.scaladsl.client.RequestBuilding.Get
import akka.http.scaladsl.model.{HttpResponse,StatusCodes}
import akka.http.scaladsl.unmarshalling.Unmarshal

import scala.util.{Failure,Success}
// for JSON serialization/deserialization following dependency is required:
// "com.typesafe.akka" %% "akka-http-spray-json" % "10.1.7"
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import spray.json.DefaultJsonProtocol._

import scala.concurrent.Future

3-定义您的自定义模型(在我的情况下,仅是个人档案模型)

final case class Profile(
                      Name: String,Surname: String,Mail: String,Age: Int,Town: String,Role: String,PrimaryInstr: String,SecondaryInstr: String,PrimaryGenre: String,SecondaryGenre: String,Influences: String,RecordLabel: String,GigAvailability: String,RehearseAvailability: String,RecordingExperience: String,MusicalAge: Int)

4-定义自定义“ unmarshaller”:计算自定义模型的属性数量,例如 n 并使用jsonFormat n 您的CustomType )。因此,在这种情况下,我们有16个属性->

implicit val profileFormat = jsonFormat16(Profile)

5-发出http请求。确保您的响应包含与模型匹配的JSON对象或objetcs的JSON数组。使用此代码检索响应并将其转换为您的自定义模型。

def getProfiles = {
    
    //Make request
    var req = Get("http://localhost:9090/profiles")
    
    //Save Response in a Future object
    val responseFuture: Future[HttpResponse] = Http().singleRequest(req)
    
    //When the Future is fulfilled
    responseFuture.onComplete {


        case Success(response) =>
          //Here your code if there is a response
          
          //Convert your response body (response.entity) in a profile array. Note that it is a Future object
          var responseAsProfiles: Future[Array[Profile]]= Unmarshal(response.entity).to[Array[Profile]]
          
          //When the Future is fulfilled
          responseAsProfiles.onComplete{

            _.get match {

              case profiles: Array[Profile] => 
                //If response was a array of Profiles you can work with profiles
                profiles.foreach[Profile] { profile =>
                  println(profile)
                  profile
                }

              case _ => println("error")
            }

          }


        case Failure(_)   => 
          //Here your code if there is not a response
          sys.error("something wrong")
      }
}

希望这将对某人有所帮助!再见!