Akka HTTP流JSON反序列化

Akka HTTP流JSON反序列化,json,akka,akka-stream,akka-http,Json,Akka,Akka Stream,Akka Http,是否可以通过TestRing将长度未知的外部流动态反序列化到域对象中 上下文 我调用一个无限长的HTTP端点,该端点输出一个不断增长的JSON数组: [ { "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" }, { "prop": true, "prop2": fal

是否可以通过TestRing将长度未知的
外部流动态反序列化到域对象中


上下文 我调用一个无限长的
HTTP
端点,该端点输出一个不断增长的
JSON数组

[
    { "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
    { "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
    { "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
    { "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
    { "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
    ...
] <- Never sees the daylight
[
{“prop”:true,“prop2”:false,“prop3”:97,“prop4”:“sample”},
{“prop”:true,“prop2”:false,“prop3”:97,“prop4”:“sample”},
{“prop”:true,“prop2”:false,“prop3”:97,“prop4”:“sample”},
{“prop”:true,“prop2”:false,“prop3”:97,“prop4”:“sample”},
{“prop”:true,“prop2”:false,“prop3”:97,“prop4”:“sample”},
...
]我想这一定对你有帮助。这个库允许通过Enumerator/Iteratee模式解析Json,当然,不必等待接收所有数据

例如,构建表示“无限”Json数组的“无限”字节流

import play.api.libs.iteratee.{Enumeratee, Enumerator, Iteratee}

var i = 0
var isFirstWas = false

val max = 10000

val stream = Enumerator("[".getBytes) andThen Enumerator.generateM {
  Future {
    i += 1
    if (i < max) {
      val json = Json.stringify(Json.obj(
        "prop" -> Random.nextBoolean(),
        "prop2" -> Random.nextBoolean(),
        "prop3" -> Random.nextInt(),
        "prop4" -> Random.alphanumeric.take(5).mkString("")
      ))

      val string = if (isFirstWas) {
        "," + json
      } else {
        isFirstWas = true
        json
      }


      Some(Codec.utf_8.encode(string))
    } else if (i == max) Some("]".getBytes) // <------ this is the last jsArray closing tag
    else None

  }
}
现在编写解析器,它将解析每个项目

import play.extras.iteratees._    
import JsonBodyParser._
import JsonIteratees._
import JsonEnumeratees._

val parser = jsArray(jsValues(jsSimpleObject)) ><> Enumeratee.map { json =>
  for {
    prop <- json.\("prop").asOpt[Boolean]
    prop2 <- json.\("prop2").asOpt[Boolean]
    prop3 <- json.\("prop3").asOpt[Int]
    prop4 <- json.\("prop4").asOpt[String]
  } yield Props(prop, prop2, prop3, prop4)
}
Jsonites包中的
Encoding.decode()
将字节解码为
CharString
<代码>结果
值具有类型<代码>枚举器[选项[项目]]
,您可以对此枚举器应用一些迭代器以开始解析过程


总之,我不知道您是如何接收字节的(解决方案在很大程度上取决于此),但我认为这表明了您的问题的可能解决方案之一。

我在尝试将Twitter流(无限字符串)解析为域对象时遇到了一个非常类似的问题。 我用如下方法解决了这个问题:

val response: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = serviceUrl))

response.onComplete {
  case Success(value) =>
    value.entity.dataBytes
      .via(JsonFraming.objectScanner(Int.MaxValue))
      .map(_.utf8String)         // In case you have ByteString
      .map(decode[MyEntity](_))  // Use any Unmarshaller here
      .grouped(20)
      .runWith(Sink.ignore)      // Do whatever you need here 
  case Failure(exception) => log.error(exception, "Api call failed")
}
案例类Tweet(用户名:字符串,地理位置:选项[Geo])
案例类别地理位置(纬度:浮动,经度:浮动)
对象Tweet{
def应用(s:字符串):Tweet={
解析(StringInput,useBigDecimalForDouble=false,useBigIntForLong=false)
}
}
然后我只是缓冲流并将其映射到Tweet:

Json4s对选项(或对象内部的自定义对象,如示例中的Geo)具有完全支持。因此,您可以像我一样放置一个选项,如果该字段没有出现在Json中,它将被设置为None


希望有帮助

我想在这种情况下应该使用
JsonFraming.objectScanner(Int.MaxValue)
。正如文件所述:

返回实现基于“大括号计数”的框架的流 用于发出有效JSON块的运算符。它扫描传入的数据 用于有效JSON对象的流,并返回ByTestRing块 只包含那些有效的块。数据的典型示例 可能需要使用此运算符进行帧处理,包括:非常大的阵列

所以你可以这样结束:

val response: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = serviceUrl))

response.onComplete {
  case Success(value) =>
    value.entity.dataBytes
      .via(JsonFraming.objectScanner(Int.MaxValue))
      .map(_.utf8String)         // In case you have ByteString
      .map(decode[MyEntity](_))  // Use any Unmarshaller here
      .grouped(20)
      .runWith(Sink.ignore)      // Do whatever you need here 
  case Failure(exception) => log.error(exception, "Api call failed")
}

为了澄清,您是尝试接收此JSON流还是广播此流?如果广播,您的内部表示是什么(例如迭代器、scala流等)?此外,通信必须是数组还是单个域对象的流?@RamonjRomeryVigil此流将完全是外部的。在您的特定情况下,您可以等待关闭
}
,并为中间的文本调用您选择的反序列化器。这需要一些操作,可能还需要对ByteString进行缓冲,但它们非常基本。您真的需要JSON数组吗?对JSON文档使用不同的分隔符(例如新行)将使此任务更容易。看@PavelKudinov,你有这样的例子吗?
val reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(inputStream), "UTF-8"))
var line = reader.readLine()
while(line != null){
    store(Tweet.apply(line))
    line = reader.readLine()
}
val response: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = serviceUrl))

response.onComplete {
  case Success(value) =>
    value.entity.dataBytes
      .via(JsonFraming.objectScanner(Int.MaxValue))
      .map(_.utf8String)         // In case you have ByteString
      .map(decode[MyEntity](_))  // Use any Unmarshaller here
      .grouped(20)
      .runWith(Sink.ignore)      // Do whatever you need here 
  case Failure(exception) => log.error(exception, "Api call failed")
}