如何将 JSON 表示为案例 class

how to represent a JSON as a case class

我想将 JSON 表示为一个案例 class。

{
result:"success" or "fail"
message: "some message"
}

我的特殊要求是 result 只能取值 "success" 或 "error"。任何其他值都应该无法解析 JSON

我尝试创建一个特征,然后对它进行子class,但我不明白如何限制 result

的值
sealed trait JSONResult 

case class JSONResultError(final val result:String="error") extends JSONResult
case class JSONResultSuccess(final val result:String="success") extends JSONResult

case class JsonMessages (
                        result: JSONResult,
                        message: String
                        )

虽然我可以创建一个 JsonMEssages 类型的变量,但我无法区分结果是 JSONResultSuccess 类型还是 JSONResultError 类型。

scala> val jm = JsonMessages(JSONResultError(),"some error message")
jm: JsonMessages = JsonMessages(JSONResultError(error),some error message)

scala>scala> jm.result.result //this doesn't work because result is of type JSONResult which hasn't got `result` though it holds object of type `JSONResultError`
<console>:13: error: value result is not a member of JSONResult
       jm.result.result
                 ^

scala> jm.result
res17: JSONResult = JSONResultError(error)

最终,我希望能够如下创建 ReadsWrites,但我不确定这是否可行。

object JSONMessagesImplicits {

  /*Writes (write to JsValue) are used by toJson method of Json object to convert data (say the model) to JsValue*/

  implicit val JsonResultErrorWrites: Writes[JSONResultError] = (JsPath \ "result").write[String](unlift(JSONResultError.unapply))
  implicit val JsonResultSuccessWrites: Writes[JSONResultSuccess] = (JsPath \ "result").write[String](unlift(JSONResultError.unapply))

  implicit val JsonMessagesWithErrorWrites: Writes[JsonMessages] = (
    (JsPath \ "result").write[JSONResultError] and
      (JsPath \ "message").write[String]) (unlift(JsonMessages.unapply))

  implicit val JsonMessagesWithSuccessWrites: Writes[JsonMessages] = (
    (JsPath \ "result").write[JSONResultSuccess] and
      (JsPath \ "message").write[String]) (unlift(JsonMessages.unapply))
}

错误消息非常明确:JSONResult 不知道 result 字段,因此无法正确序列化。

JSONResult不一定是trait,还是可以转化成抽象的class,问题应该解决了,例如:

sealed abstract class JsonResult(val result: String)

case class JsonResultSuccess(override val result: String = "success") extends JsonResult(result)
case class JsonResultError(override val result: String = "error") extends JsonResult(result)

case class JsonMessage(val result: JsonResult, val message: String)

然后,不需要为所有的东西创建单独的Write,可以缩短为这个:

implicit val resultWrites = new Writes[JsonMessage] {
  override def writes(message: JsonMessage) : JsValue = {
    Json.obj(
      "result" -> message.result.result,
      "message" -> message.message
    )
  }
}

更新

如果您仍然want/have使用函数式语法定义Writes的方法,则仍有一定的简化空间。

我认为创建 JSONResult 实体只是为了包装 "error" 或 "success" 字符串有点矫枉过正,它使进一步的映射复杂化,正如您看到的那样,多个 Writes 在原始问题中。

我们可以想象密封的东西是这样的:

sealed abstract class JsonMessage(val result: String, val message: String)

case class JsonResultSuccess(override val message: String) extends JsonMessage("success", message)
case class JsonResultFailure(override val message: String) extends JsonMessage("failure", message)

是的,message 字段现在与根级别的 result 结合在一起; result 值在后代中是常量。

我们还需要实施 unapply fo JsonMessage:

object JsonMessage {

  def unapply(jsonMessage: JsonMessage): Option[(String, String)] = {
    if (jsonMessage == null) None
    else Some((jsonMessage.result, jsonMessage.message))
  }

}

经过这些修改后,我们只有一个Writes可以使用原来的样式来定义:

implicit val messageWrites: Writes[JsonMessage] =
  ((JsPath \ "result").write[String] and
   (JsPath \ "message").write[String]) (unlift(JsonMessage.unapply))

希望这对您有所帮助。

End-of-update