如何使用 Akka HTTP 解组 json 响应删除不必要的字段

Posted

技术标签:

【中文标题】如何使用 Akka HTTP 解组 json 响应删除不必要的字段【英文标题】:How to unmarshall json response removing unnecessary fields using Akka HTTP 【发布时间】:2022-01-10 08:02:39 【问题描述】:

我是 Akka HTTP 的新手,我想从 JSON 响应中删除不必要的字段,只获取必要的字段。例如,我使用this 端点来获取响应,它包含一堆字段。目前我只需要“名称”和“版本”。我想知道如何将其反序列化为仅包含“名称”和“版本”的案例类。我编写了以下几行代码以将响应作为字符串获取。

import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpRequest, HttpResponse
import akka.stream.scaladsl.Flow, Sink, Source
import akka.stream.ActorMaterializer, OverflowStrategy

import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
import scala.util.Failure, Success

object SoftwareRegistry extends App 

  implicit val system = ActorSystem("NPMRegistry")
  implicit val materializer = ActorMaterializer()

  import system.dispatcher

  case class NPMPackage(name: String)

  // reading the packages
  val filename = "B:\\Scala\\NPMRegistry\\src\\main\\resources\\packages.txt"
  val bufferedSource = scala.io.Source.fromFile(filename)
  val listOfPackages: List[NPMPackage] = (for (line <- bufferedSource.getLines) yield 
    NPMPackage(line.trim)
  ).toList
  bufferedSource.close()

  // source
  val sourceList = Source(listOfPackages)

  // sink
  val sink = Sink.foreach[NPMPackage]  p =>
    // https request
    val responseFuture: Future[HttpResponse] =
      Http().singleRequest(HttpRequest(uri = s"https://registry.npmjs.org/$p.name"))
    val x = responseFuture
      .flatMap(_.entity.toStrict(2 seconds))
      .map(_.data.utf8String)
    x.onComplete 
      case Success(res) => println(res)
      case Failure(_) => sys.error("Something went wrong")
    
  

  // flow to slow things down and streaming sink to time-delayed operations
  val bufferedFlow = Flow[NPMPackage]
    .buffer(10, overflowStrategy = OverflowStrategy.backpressure)
    .throttle(1, 3 seconds)

  sourceList.async
    .via(bufferedFlow).async
    .to(sink)
    .run()

它会打印以下输出

【问题讨论】:

预期的数据类型是什么?在这种特殊情况下,“版本”本身就是一个对象,您需要该对象提供什么? @JohnyTKoshy 我需要每个版本的 'dependencies' 和 'devDependencies' 啊.. 这些又是对象。我发布了一个答案,其版本为List。其余的你也许能弄清楚。 @JohnyTKoshy 这似乎有效,我试图找出嵌套对象类型,这让我有点困惑。我创建了以下数据结构。 case class Package(name: String, versions: List[Version]) case class Version(version: String, dependencies: List[String], devDependencies: List[String])。你介意帮我按照这些数据结构写吗? dependenciesdevDependencies 不是字符串,它们是 javascript 对象。 【参考方案1】:

为了解析json,你需要使用一些库。在akka-http 文档中,他们使用spray-json。使用适当的akkaHttpVersion 将以下依赖项添加到您的build.sbt

"com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion

现在您的数据需要序列化器和反序列化器。我正在使用一个简单的模型,根据需要更改它。

trait Formatter extends DefaultJsonProtocol 

  implicit object jsonFormat extends JsonFormat[Versions] 
    override def read(json: JsValue): Versions = json match 
      case JsObject(fields) =>
        Versions(fields.keys.toList)
    

    override def write(obj: Versions): JsValue = JsonParser(obj.toString)
  

  implicit val formatterPackage: RootJsonFormat[Package] = jsonFormat2(Package)

  case class Package(name: String, versions: Versions)

  case class Versions(versions: List[String])

终于sink:

 //needed import with others
 import spray.json._

 object SoftwareRegistry extends App  with Formatter 

   //existing code
   //---------


   val sink = Sink.foreach[NPMPackage]  p =>
       // https request
       val responseFuture: Future[HttpResponse] =
         Http().singleRequest(HttpRequest(uri = s"https://registry.npmjs.org/$p.name"))
       val packages = responseFuture
         .flatMap(
           _.entity
             .dataBytes
             .via(JsonFraming.objectScanner(Int.MaxValue))
             .map(_.utf8String)
             .map(_.parseJson.convertTo[Package])
             .toMat(Sink.seq)(Keep.right)
             .run()
         )

       packages.onComplete 
         case Success(res) => println(res)
         case Failure(_) => sys.error("Something went wrong")
       
   

   //existing code
   //---------

【讨论】:

以上是关于如何使用 Akka HTTP 解组 json 响应删除不必要的字段的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Akka HTTP 中将“text/plain”解组为 JSON

喷洒 Akka Json 解组

akka-http:找不到参数解组的隐式值

当 JSON 字段键是日期时,如何将 JSON 对象解组为 Golang 结构?

如何使用 AKKA-HTTP、spray-json、oauth2 和 slick 优化 scala REST api?

akka-http 分块响应连接