Akka HTTP流JSON反序列化
是否可以通过TestRing将长度未知的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
外部流动态反序列化到域对象中
上下文
我调用一个无限长的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")
}