Декодирование JSON в Elm Возможно
Учитывая следующий JSON:
[
{
"id": 0,
"name": "Item 1",
"desc": "The first item"
},
{
"id": 1,
"name": "Item 2"
}
]
Как вы декодируете это в следующую модель:
type alias Model =
{ id : Int
, name : String
, desc : Maybe String
}
Ответы
Ответ 1
Брайан Хикс имеет серию сообщений на декодерах JSON, вы, вероятно, захотите специально посмотреть на Adding New Fields to Your JSON Decoder
(который обрабатывает сценарий, в котором вы можете или не можете получать поле от объекта JSON).
Для начала, вы, вероятно, захотите использовать пакет elm-decode-pipe. Затем вы можете использовать функцию optional
, чтобы заявить, что в поле desc
может быть не так. Как отмечает Брайан в статье, вы можете использовать декодер maybe
из базовый пакет Json.Decode
, но он будет производить Nothing
для любого сбоя, а не только null
. Существует декодер nullable
, который вы также можете рассмотреть, если вы не хотите использовать модуль конвейера.
Ваш декодер может выглядеть примерно так:
modelDecoder : Decoder Model
modelDecoder =
decode Model
|> required "id" int
|> required "name" string
|> optional "desc" (Json.map Just string) Nothing
Вот живой пример на Элли.
Ответ 2
Поэтому, если вы ищете решение с нулевой зависимостью, которое не требует Json.Decode.Pipeline
.
import Json.Decode as Decode exposing (Decoder)
modelDecoder : Decoder Model
modelDecoder =
Decode.map3 Model
(Decode.field "id" Decode.int)
(Decode.field "name" Decode.string)
(Decode.maybe (Decode.field "desc" Decode.string))
Если вы хотите сделать это, используя конструктор Model
в качестве аппликативного функтора (потому что вам нужно больше 8 элементов).
import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Extra as Decode
modelDecoder : Decoder Model
modelDecoder =
Decode.succeed Model
|> Decode.andMap (Decode.field "id" Decode.int)
|> Decode.andMap (Decode.field "name" Decode.string)
|> Decode.andMap (Decode.maybe (Decode.field "desc" Decode.string))
И то, и другое можно использовать с List
с Decode.list modelDecoder
. Я хотел бы, чтобы аппликативные функции были в стандартной библиотеке, но вам нужно будет обратиться ко всем библиотекам * -extra, чтобы получить эти функции. Знание того, как работают аппликативные функторы, поможет вам лучше разобраться в этом, поэтому я бы посоветовал прочитать о них. Решение Decode Pipeline абстрагирует эту простую концепцию, но когда вы Result.andMap
с необходимостью Result.andMap
или любого другого из andMap
потому что нет mapN
для вашего модуля или DSL, вы будете знать, как добраться до вашего решения.
Из-за применимости декодеров все поля должны обрабатываться асинхронно и параллельно с небольшим выигрышем в производительности, а не синхронно, как andThen
.
Под капотом JSON.Decode.Pipeline
использует Json.Decode.map2
(то есть andMap
), поэтому разницы в производительности нет, но использует DSL, который незначительно более "дружелюбен".
Ответ 3
Брайан Хикс "Добавление новых полей в ваш декодер JSON" помогло мне разработать следующее. Для рабочего примера см. Ellie
import Html exposing (..)
import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Pipeline as JP
import String
type alias Item =
{ id : Int
, name : String
, desc : Maybe String
}
main =
Decode.decodeString (Decode.list itemDecoder) payload
|> toString
|> String.append "JSON "
|> text
itemDecoder : Decoder Item
itemDecoder =
JP.decode Item
|> JP.required "id" Decode.int
|> JP.required "name" Decode.string
|> JP.optional "desc" (Decode.map Just Decode.string) Nothing