Argonaut, сопоставление пользовательского формата JSON

Модель:

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?


person Tvaroh    schedule 24.10.2013    source источник


Ответы (1)


Следующее нестандартно, но оно должно работать. Обратите внимание, что я предполагаю, что вам просто нужна пустая строка по обе стороны от разделителя, когда это значение пусто, и я не проверяю, что значение месяца находится между 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, но с ним также довольно легко работать.

person Travis Brown    schedule 25.10.2013
comment
Именно то, что я искал. Спасибо, Трэвис. Оператор ~ тоже отличная находка для меня. - person Tvaroh; 25.10.2013