argonaut - разделение неудач и успехов при извлечении List[A]

У меня есть экземпляр 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]] и посмотреть, заинтересован ли кто-то.


person ceedubs    schedule 19.11.2014    source источник


Ответы (1)


Вы можете использовать декодер HCursor, а не декодер Json, который сохраняет историю:

j.as[List[HCursor]].map(_.map(_.as[Int].result).separate)
person tixxit    schedule 19.11.2014