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

How to unmarshall custom object from Http Response Entity in Scala?

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

自定义 class 是配置文件,其中包含配置文件列表。

  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])

我尝试使用以下代码解组到配置文件,但它无法编译 因为错误

could not find implicit value for parameter 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","Surname":"D'Ambrosio", ..., "MusicalAge":24}]) FulfilledFuture([{"Name":"Amadeus","Surname":"Rapisarda", ..., "MusicalAge":9},{"Name":"Federico","Surname":"D'Ambrosio", ..., "MusicalAge":24}])

我怎样才能获得 Profiles 对象? 提前致谢!

我终于找到了一个很好用的解决方案。

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 - 定义您的自定义模型(在我的例子中只有 Profile 模型)

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 并使用 jsonFormatn(您的自定义类型)。所以在这种情况下我们有 16 个属性 -->

implicit val profileFormat = jsonFormat16(Profile)

5 - 发出 http 请求。确保您的响应包含与您的模型匹配的 JSON 对象或 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")
      }
}

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