我正致力于使用API,该API在其内部的多个层中公开对象的响应。例如,对于某些回复,我们会回复:
{
"error": {
"code": "123",
"description": "Description"
}
}
但在其他情况下,它会回复:
{
"data": [
{
"message_id": "123",
"error": {
"code": "123",
"description": "Description"
}
}
]
}
在这两种情况下,错误对象都是相同的,在这两种情况下,我实际上并不关心其余的有效负载。我希望使用\\递归JsPath运算符,但下面的实现失败:
case class ErrorMessage(code: String, description: String)
implicit val errorMessageFormat = Json.format[ErrorMessage]
case class ErrorResponse(errors: Seq[ErrorMessage])
implicit val errorResponseFormat: Format[ErrorResponse] = Format(
(__ \\ "error").read[Seq[ErrorMessage]].map(ErrorResponse),
(__ \ "errors").write[Seq[ErrorMessage]].contramap((r: ErrorResponse) => r.errors)
)
这会出错:
JsError(List((//error,List(ValidationError(List(error.expected.jsarray),WrappedArray())))))
我理解为什么:(__ \\ "error")
返回Seq[JsValue]
,我的阅读电话预计会有JsArray
。
这是一个很好的方式吗?
答案 0 :(得分:0)
由于第一块已经是Seq,因此只需映射内部元素,就像映射单个对象一样。我对框架不是很熟悉(我自己大多使用Json4s),但你的描述听起来像是
ErrorResponse((__ \\ "error").map(_.read[Seq[ErrorMessage]]))
应该更接近您想要的内容。(__ \\ "error")
为您提供Seq
JsValues
,map
为每个JsValue
,read
做一些事情将单个JsValue
转换为ErrorMessage
,并将生成的Seq[ErrorMessage]
转换为ErrorResponse
构造函数。
答案 1 :(得分:0)
因此对于任何尝试做类似事情的人来说,下面的工作方式。
val errorResponseReads = Reads[ErrorResponse] { value: JsValue =>
val errorsJsArray: JsArray = JsArray(value \\ "error")
errorsJsArray.validate[Seq[ErrorMessage]].map(ErrorResponse)
}
然后格式变为:
implicit val errorResponseFormat: Format[ErrorResponse] = Format(
errorResponseReads,
(__ \ "errors").write[Seq[ErrorMessage]].contramap((r: ErrorResponse) => r.errors)
)
基本上,您需要定义明确使用的Read。在读取中,您可以使用递归搜索返回Seq[JsValue]
,然后创建可以正常验证的JsArray
。
这样可以正常工作,但如果我们不必定义单独的Reads [T]那将会很好。