Здесь есть две подзадачи: 1. декодирование списка и 2. преобразование его в нужную вам форму.Вы можете сделать это, как подсказывает @SimonH, расшифровав список значений JSON, постобработав его и затем (или во время постобработки) декодировать внутренние значения.Вместо этого я предпочел бы сначала полностью декодировать его в пользовательский тип, а затем полностью выполнить постобработку в области типов Elm.
Итак, шаг 1, декодирование:
type JsonListValue
= String String
| Int Int
decodeListValue : Decode.Decoder JsonListValue
decodeListValue =
Decode.oneOf
[ Decode.string |> Decode.map String
, Decode.int |> Decode.map Int
]
decoder : Decode.Decoder (List (List JsonListValue))
decoder =
Decode.list (Decode.list decodeListValue)
Это базовый шаблон, который вы можете использовать для декодирования любого гетерогенного массива.Просто используйте oneOf
, чтобы попробовать список декодеров по порядку, и сопоставьте каждое декодированное значение общему типу, обычно пользовательскому типу с простым конструктором для каждого типа значения.
Затем на шаге 2преобразование:
extractInts : List JsonListValue -> List Int
extractInts list =
list
|> List.foldr
(\item acc ->
case item of
Int n ->
n :: acc
_ ->
acc
)
[]
postProcess : List JsonListValue -> Result String ( String, List Int )
postProcess list =
case list of
(String first) :: rest ->
Ok ( first, extractInts rest )
_ ->
Err "first item is not a string"
postProcess
сопоставит первый элемент с String
, запустите extractInts
с остальными, которые все должны быть Int
с, затем соедините их вместе в кортеж, который выхочу.Если первый элемент не является String
, он вернет ошибку.
extractInts
сворачивает каждый элемент и добавляет его в список, если это Int
, и игнорирует его в противном случае.Обратите внимание, что он не возвращает ошибку, если элемент не является Int
, он просто не включает его.
Обе эти функции могли быть написаны так, чтобы либо завершиться сбоем, если значения не соответствуютк ожиданиям, как postProcess
, или обрабатывать это "изящно", как extractInts
.Я решил сделать один из них, просто чтобы проиллюстрировать, как вы могли бы сделать и то, и другое.
Затем, шаг 3, это собрать его вместе:
Decode.decodeString decoder json
|> Result.mapError Decode.errorToString
|> Result.andThen
(List.map postProcess >> Result.Extra.combine)
Здесь Result.mapError
используется дляошибка от декодирования для соответствия типу ошибки, которую мы получаем из postProcess
.Result.Extra.combine
- это функция от elm-community/result-extra
, которая превращает List
из Result
с в Result
из List
, что очень удобно здесь.