Декодирование 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