我正在使用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!
)
不幸的是,ModelSubclassA
和ModelSubclassB
都与其他案例类有多个关联,并且在此示例编译时,在尝试解析这些子类型时,它在运行时失败。总之,将有几十个案例类构成返回数据的层次结构。
我也尝试用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<:具有Serializable with Model的产品, 但类DecodeResult在类型A中是不变的。您可能希望定义 A代替+ A. (SLS 4.5) ||| c.as [ModelSubclassB])。asInstanceOf [DecodeResult [模型]] ^
所以我开始查看源代码并意识到DecodeResult
的定义已更改为包含{6.2}版本中错误所建议的+A
。不幸的是,升级到该版本会使所有Model
子类编解码器变成含糊不清的含义,这是有道理的。
啊...
答案 0 :(得分:0)
答案需要两件:
“Sum Types”将编解码器的值和距离封装在用于返回值的类型中。在上面的示例中,编解码器使用Model
特征来解决implicits。如果它也用作返回类型,则会引入编译器无法明确解析的递归定义。
一旦使用了总和类型,客户端就可以轻松接受这些类型并使用match
中的提取器来获取其中的实际值。