Question

Model:

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

Argonaut-based decoder:

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

This allows to parse such format:

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

Now I want to simplify JSON format and use

"2013/10"

instead, but leave my model unchanged. How to accomplish this with Argonaut?

Was it helpful?

Solution

The following is off the cuff but it should work. Note that I'm assuming that you just want an empty string on either side of the divider when that value is empty, and I'm not validating that the month value is between 1 and 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)
  }
)

And then:

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

Which gives us:

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

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

As expected.

The trick is to provide your own encoding and decoding functions to CodecJson.apply. The encoding function is very straightforward—it just takes something of the encoded type and returns a Json value. The decoding method is a little more complicated, since it takes an HCursor and returns a DecodeResult, but these are also pretty easy to work with.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top