Argonaut,自定义JSON格式映射

时间:2013-10-24 19:18:48

标签: json scala scalaz argonaut

型号:

case class DateValue(year: Option[Int] = None, month: Option[Int] = None)

基于Argonaut的解码器:

implicit val dateValueDecode = casecodec2(DateValue.apply, DateValue.unapply)("year", "month")

这允许解析这样的格式:

{
  "year": "2013",
  "month": "10"
}

现在我想简化JSON格式并使用

"2013/10"

相反,但保持我的模型不变。如何用Argonaut完成这个?

1 个答案:

答案 0 :(得分:2)

以下是袖口,但它应该工作。请注意,我假设你只想在该值为空时在分隔符的任一侧使用空字符串,并且我没有验证月份值是否在1到12之间。

import argonaut._, Argonaut._
import scalaz._, Scalaz._

case class DateValue(year: Option[Int] = None, month: Option[Int] = None)

object YearMonth {
  def unapplySeq(s: String) =
    """((?:\d\d\d\d)?)/((?:\d?\d)?)""".r.unapplySeq(s).map {
      case List("", "") => List(None, None)
      case List(y, "") => List(Some(y.toInt), None)
      case List("", m) => List(None, Some(m.toInt))
      case List(y, m) => List(Some(y.toInt), Some(m.toInt))
  }
}

implicit val DateValueCodecJson: CodecJson[DateValue] = CodecJson(
  { case DateValue(year, month) => jString(~year + "/" + ~month) },
  c => c.as[String].flatMap {
    case YearMonth(y, m) => DecodeResult.ok(DateValue(y, m))
    case _ => DecodeResult.fail("Not a valid date value!", c.history)
  }
)

然后:

val there = Parse.decodeValidation[DateValue](""""2013/12"""")
val back = there.map(DateValueCodecJson.encode)

这给了我们:

scala> println(there)
Success(DateValue(Some(2013),Some(12)))

scala> println(back)
Success("2013/12")

正如所料。

诀窍是为CodecJson.apply提供自己的编码和解码功能。编码函数非常简单 - 它只需要一些编码类型并返回Json值。解码方法稍微复杂一些,因为它需要HCursor并返回DecodeResult,但这些也很容易使用。

相关问题