У меня есть экземпляр DecodeJson[A]
для определенного типа, A
, и у меня есть экземпляр Json
, назовем его j
. Я хотел бы сделать что-то вроде j.as[List[A]]
. Однако этот JSON поступает ко мне от третьей стороны, и если в некоторых элементах массива отсутствуют поля, я хотел бы просто регистрировать ошибки для этих элементов и собирать только те элементы, которые могут быть правильно проанализированы (вместо провал всего декодирования).
Мне кажется, что я хочу получить что-то вроде (List[(String, CursorHistory)], List[A])
. То есть любые успешно декодированные элементы окажутся в списке справа, а ошибки для любых элементов, которые не удалось успешно разобрать, будут накапливаться слева. Это очень похоже на метод MonadPlus.separate
в scalaz.
Я могу придумать пару способов добраться туда. Один из способов - сделать что-то вроде:
j.as[List[Json]].map(_.map(_.as[A].result).separate)
Это должно дать мне DecodeResult[List[(String, CursorHistory)], List[A]]
по желанию. Однако при этом будет потеряна информация курсора о том, где в массиве JSON были расположены элементы, которые не удалось выполнить. Я мог бы использовать что-то вроде zipWithIndex
, но это становится довольно грязным.
Мне кажется, что лучшим решением является сделать что-то вроде этого:
implicit def DecodeResultDecodeJson[A: DecodeJson]: DecodeJson[DecodeResult[A]] =
decodeArr(_.as[A]) // outer DecodeResult is always a success
j.as[List[DecodeResult[A]]].map(_.map(_.result)).separate)
Это даст мне тот же тип вывода, но CursorHistory
должен быть заполнен, чтобы включить информацию о шагах в элементы массива.
Вот пример:
val j = Json.array(List("1", "2", "foo").map(Json.jString): _*)
val r = j.as[List[DecodeResult[Int]]].map(_.map(_.result).separate)
// r is DecodeResult[(List[(String, CursorHistory)], List[Int])] =
// DecodeResult(\/-((List((Int,CursorHistory([->,->,\\]))),List(1, 2))))
Кажется, это ведет себя разумно (игнорируя какую-то ужасную конкатенацию List, которая, вероятно, происходит). Однако похоже, что такие вещи будут довольно распространенным вариантом использования, поэтому мне интересно, есть ли более простой или встроенный способ сделать что-то подобное. Если нет, я мог бы отправить PR argonaut для экземпляра DecodeJson[DecodeJson[A]]
и посмотреть, заинтересован ли кто-то.