使用 Play / Scala JSON 格式处理 Either 对象

9

我有一个值类,它接受一个 Either。我想为它生成适用于 Play for Scala v2.5.6 的 JSON Format

import org.joda.time.{DateTime, Duration}

case class When(when: Either[DateTime, Duration]) extends AnyVal

我认为我已经掌握了writes方法;我遇到的问题在于reads方法。我尝试了两种不同的方法,但都失败了。

尝试 #1,展示了readswrites两个方法:

import play.api.libs.json._
import play.api.libs.json.Json.obj

object When {    
  def apply(dateTime: DateTime): When = When(Left(dateTime))

  def apply(duration: Duration): When = When(Right(duration))

  implicit val whenFormat = new Format[When] {
    def reads(json: JsValue): JsResult[When] = {
      val reads = (__ \ "dateTime").read[Long] { (millis: Long) =>
        When(Left(new DateTime(millis)))
      } | (__ \ "duration").read[Long] { (millis: Long) =>
        When(Right(new Duration(millis)))
      }
      reads.reads(json)
    }

    def writes(o: When): JsValue = obj(
      o.when.fold(
        duration => "duration" -> duration.getMillis,
        dateTime => "dateTime" -> dateTime.getMillis
      )
    )
  }
}

错误信息如下:
overloaded method value read with alternatives:
[error]   (t: Long)play.api.libs.json.Reads[Long] <and>
[error]   (implicit r: play.api.libs.json.Reads[Long])play.api.libs.json.Reads[Long]
[error]  cannot be applied to (Long => When)
[error]       val reads = (__ \ "dateTime").read[Long] { (millis: Long) =>

[error] overloaded method value read with alternatives:
[error]   (t: Long)play.api.libs.json.Reads[Long] <and>
[error]   (implicit r: play.api.libs.json.Reads[Long])play.api.libs.json.Reads[Long]
[error]  cannot be applied to (Long => When)
[error]       } | (__ \ "duration").read[Long] { (millis: Long) =>

尝试 #2,仅展示reads方法:
def reads(json: JsValue): JsResult[When] =
  JsSuccess(
    When(Left(new DateTime((__ \ "dateTime").read[Long]))) ||
    When(Right(new Duration((__ \ "duration").read[Long])))
  )

错误信息如下:
value || is not a member of When
[error]  Note: implicit value whenFormat is not applicable here because it comes after the application point and it lacks an explicit result type
[error] Error occurred in an application involving default arguments.

我只想要一个能够工作的方法,不在意使用哪种方法(即使是我没有展示的方法),只要它是可维护且高效的。如果能知道每种方法的问题所在也会很有帮助。


尝试从"When"中删除"extends AnyVal"。 - Łukasz
移除extends AnyVal对于任何一种方法都没有产生影响。 - Mike Slinn
1个回答

10

这是如何做到这一点的工作示例:

import org.joda.time.{DateTime, Duration}
import play.api.libs.functional.syntax._
import play.api.libs.json.Reads._
import play.api.libs.json._

object When {
  def apply(dateTime: DateTime): When = When(Left(dateTime))

  def apply(duration: Duration): When = When(Right(duration))

  val reads: Reads[When] = 
    (__ \ "dateTime").read[Long].map(millis => When(Left(new DateTime(millis)))) |
    (__ \ "duration").read[Long].map(millis => When(Right(new Duration(millis))))

  val writes: Writes[When] = new Writes[When] {
    override def writes(o: When): JsValue = Json.obj(
      o.when.fold(
        duration => "duration" -> duration.getMillis,
        dateTime => "dateTime" -> dateTime.getMillis
      )
    )
  }

  implicit val format = Format(reads, writes)
}

基本上,您应该对读取进行映射。

(__ \ "dateTime").read[Long]

给你一个 Reads[Long],然后你可以将结果映射到When。你只是传递参数。这个参数可以是一个Long,以忽略所读取的内容并返回该值,或者可以是隐式读取long类型,你可能不想更改它,应该让它保持隐式。

因此,您可以按类似的方式创建另一个用于时间长度的reads,并使用备选项 (|) 结合在一起,完成后,您就拥有了reads。

你的第二种方法没有意义。要么使用reads并组合它们,要么手动检查是否存在某些内容,如果不存在则返回不同的结果,但这不值得做,只需使用默认方法即可。


太好了!我已将您的答案标记为被接受的解决方案。顺便说一下,您展示的第一行代码格式不正确。 - Mike Slinn
翻译:哎呀,我太匆忙了。| 运算符未定义。 - Mike Slinn
你需要导入语法,请查看更新。我也修复了格式,谢谢。 - Łukasz
谢谢您提供导入!当回答包含一个完整的工作示例时,它确实非常有帮助:) - TonyC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接