我有一个Source[ByteString, _]
来自输入文件,有3行像这样(实际上输入是带有连续流的TCP套接字):
{"a":[2
33]
}
现在的问题是我想将它解析为Source[ChangeMessage,_]
,但是我发现的唯一示例是在每个行都有一个完整的JSON消息时处理,而不是每个JSON消息可以在多个上分段行。
我找到的一个例子是这个this库,但它希望}
或,
作为最后一个字符,即每行一个JSON。以下示例显示了此设置。
"My decoder" should "decode chunked json" in {
implicit val sys = ActorSystem("test")
implicit val mat = ActorMaterializer()
val file = Paths.get("chunked_json_stream.json")
val data = FileIO.fromPath(file)
.via(CirceStreamSupport.decode[ChangeMessage])
.runWith(TestSink.probe[ChangeMessage])
.request(1)
.expectComplete()
}
另一种选择是使用折叠和平衡}
,并且仅在完成整个JSON时发出。这个问题是折叠运算符仅在流完成时发出,因为这是一个连续的流,所以我不能在这里使用它。
我的问题是:解析分块JSON流的最快方法是什么 在AKKA Stream中,是否有任何可用的软件 这个?如果可能,我想使用circe
答案 0 :(得分:3)
正如knutwalker/akka-stream-json的文档所说:
这个流程甚至支持在它们可能到达的任何碎片中解析多个json文档,这对于使用基于流/ sse的API非常有用。
在您的情况下,您只需要分隔传入的ByteStrings:
"My decoder" should "decode chunked json" in {
implicit val sys = ActorSystem("test")
implicit val mat = ActorMaterializer()
val file = Paths.get("chunked_json_stream.json")
val sourceUnderTest =
FileIO.fromPath(file)
.via(Framing.delimiter(ByteString("\n"), 8192, allowTruncation = true))
.via(CirceStreamSupport.decode[ChangeMessage])
sourceUnderTest
.runWith(TestSink.probe[ChangeMessage])
.request(1)
.expectNext(ChangeMessage(List(233)))
.expectComplete()
}
那是因为从文件中读取时,ByteString元素包含多行,因此Circe无法解析格式错误的jsons。当您按新行划分时,流中的每个元素都是一个单独的行,因此Circe可以使用前面提到的功能对其进行解析。
答案 1 :(得分:0)
不幸的是,我不知道任何支持基于流的JSON解析的Scala库。对我来说似乎对Google Gson提供了一些支持,但我并不完全确定它可以正确处理“损坏”的输入。
然而,您可以做的是以流式方式收集JSON文档,类似于Framing.delimiter
。这与您提到的替代方法非常相似,但它没有使用fold()
;如果你这样做,你可能需要模仿Framing.delimiter
做什么,但你不需要寻找单个分隔符,你需要平衡花括号(如果可以使用顶级数组,可以选择括号),缓冲中间数据,直到整个文档通过,您将作为适合解析的单个块发出。
正如旁注所示,适合在Akka Streams中使用的流式JSON解析器的适当接口可能如下所示:
trait Parser {
def update(data: Array[Byte]) // or String
def pull(): Option[Either[Error, JsonEvent]]
}
其中pull()
返回None
如果它不能再读取但传入文档中没有实际的语法错误,JsonEvent
是用于描述流解析器事件的标准结构(即具有类似BeginObject
,BeginArray
,EndObject
,EndArray
,String
等子类的密封特征。如果您找到这样的库或创建一个库,您可以使用它来解析来自ByteString
的Akka流的数据。