Предположим, мне нужно декодировать массивы JSON, которые выглядят следующим образом, где есть пара полей в начале, некоторое произвольное количество однородных элементов, а затем еще какое-то поле:
[ "Foo", "McBar", true, false, false, false, true, 137 ]
Я не знаю, почему кто-то решил кодировать свои данные таким образом, но люди делают странные вещи, и предположим, что в этом случае мне просто нужно с этим иметь дело.
Я хочу декодировать этот JSON в такой класс case:
case class Foo(firstName: String, lastName: String, age: Int, stuff: List[Boolean])
Мы можем написать что-то вроде этого:
import cats.syntax.either._
import io.circe.{ Decoder, DecodingFailure, Json }
implicit val fooDecoder: Decoder[Foo] = Decoder.instance { c =>
c.focus.flatMap(_.asArray) match {
case Some(fnJ +: lnJ +: rest) =>
rest.reverse match {
case ageJ +: stuffJ =>
for {
fn <- fnJ.as[String]
ln <- lnJ.as[String]
age <- ageJ.as[Int]
stuff <- Json.fromValues(stuffJ.reverse).as[List[Boolean]]
} yield Foo(fn, ln, age, stuff)
case _ => Left(DecodingFailure("Foo", c.history))
}
case None => Left(DecodingFailure("Foo", c.history))
}
}
… Который работает:
scala> fooDecoder.decodeJson(json"""[ "Foo", "McBar", true, false, 137 ]""")
res3: io.circe.Decoder.Result[Foo] = Right(Foo(Foo,McBar,137,List(true, false)))
Но тьфу, это ужасно. Также сообщения об ошибках совершенно бесполезны:
scala> fooDecoder.decodeJson(json"""[ "Foo", "McBar", true, false ]""")
res4: io.circe.Decoder.Result[Foo] = Left(DecodingFailure(Int, List()))
Конечно, есть способ сделать это, который не включает переключение между курсорами и значениями Json, отбрасывание истории в наших сообщениях об ошибках и просто раздражение глаз?
Некоторый контекст: вопросы о написании пользовательских декодеров массивов JSON, подобных этому в circe, возникают довольно часто (например, этим утром < / а>). Конкретные детали того, как это сделать, вероятно, изменятся в следующей версии circe (хотя API будет аналогичным; см. этот экспериментальный проект для некоторых деталей), поэтому я действительно не хочу тратить много времени на добавление такого примера в документацию, но его достаточно, чтобы я думаю, что он заслуживает переполнения стека Вопросы и ответы.