使用AKKA Stream解码分块JSON

时间:2017-05-26 08:13:12

标签: json scala akka akka-stream

我有一个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

2 个答案:

答案 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是用于描述流解析器事件的标准结构(即具有类似BeginObjectBeginArrayEndObjectEndArrayString等子类的密封特征。如果您找到这样的库或创建一个库,您可以使用它来解析来自ByteString的Akka流的数据。