使用Argonaut解析JSON流

时间:2016-02-20 05:26:44

标签: json scala parsing argonaut

我正在使用Argonaut来解析来自远程JSON提供程序的对象。 API有两种类型的端点,一种是URL上的传统REST请求,另一种是单个JSON对象的响应。我能够在这种类型的端点上使用Argonaut轻松解析复杂的JSON返回对象。

我的问题在于提供者的流端点,它从给定端点的有界JSON集返回随机JSON对象。对象按照它们在站点上出现的顺序返回,并且可以随时返回大约20个不同对象中的任何一个。

通过API,我找不到使用Argonaut来解决这个问题的方法。 API似乎都需要类型参数化,这在下一个对象的类型无法预测的环境中很难实现。一种选择是根据JSON的每个块中的前几个字符分派到不同的编解码器,但是这破坏了将JSON字符串发送到解析器并获得对象的目标。

到目前为止,我能找到的最好的是让所有顶级案例类扩展为空trait

implicit def ModelDecodeJson: DecodeJson[Model] =
  DecodeJson(c =>
    c.as[ModelSubclassA].asInstanceOf[DecodeResult[Model]]
      ||| c.as[ModelSubclassB].asInstanceOf[DecodeResult[Model]]
      // many more here!
  )

不幸的是,ModelSubclassAModelSubclassB都与其他案例类有多个关联,并且在此示例编译时,在尝试解析这些子类型时,它在运行时失败。总之,将有几十个案例类构成返回数据的层次结构。

我也尝试用for理解来构建它,但也没有运气。

有人可以在这里建议更好的模式吗?

更新

以下似乎具有更具伸缩性的模式,但类型不合作:

implicit def ModelDecodeJson: DecodeJson[Model] =
  DecodeJson(c =>
    (c.as[ModelSubclassA] ||| c.as[ModelSubclassB]).asInstanceOf[DecodeResult[Model]]
  )
  

错误:(10,17)类型不匹配;发现:   argonaut.DecodeResult [ModelSubclassB]   必需:argonaut.DecodeResult [带序列化模型的产品]注意:   ModelSubclassB<:具有Seri​​alizable with Model的产品,   但类DecodeResult在类型A中是不变的。您可能希望定义   A代替+ A. (SLS 4.5)           ||| c.as [ModelSubclassB])。asInstanceOf [DecodeResult [模型]]                   ^

所以我开始查看源代码并意识到DecodeResult的定义已更改为包含{6.2}版本中错误所建议的+A。不幸的是,升级到该版本会使所有Model子类编解码器变成含糊不清的含义,这是有道理的。

啊...

1 个答案:

答案 0 :(得分:0)

答案需要两件:

  1. “Sum Types”将编解码器的值和距离封装在用于返回值的类型中。在上面的示例中,编解码器使用Model特征来解决implicits。如果它也用作返回类型,则会引入编译器无法明确解析的递归定义。

  2. 一旦使用了总和类型,客户端就可以轻松接受这些类型并使用match中的提取器来获取其中的实际值。