Продолжим говорить о Elm 0.18. Elm. Удобный и неловкий
Elm. Удобный и неловкий. Композиция
Elm. Удобный и неловкий. Http, Task В этой статье рассмотрим вопросы энкодеров/декодеров. Декодеры/энкодеры используются для: Как было описано ранее, Elm требует от нас обязательного преобразования внешних данных во внутренние типы приложения. За данный процесс отвечает модуль Json.Decode. Обратный процесс — Json.Encode. Тип определяющий правила декодирования — Json.Decode.Decoder a. Данный тип параметризуется пользовательским типом и определяет каким образом из JSON объекта получить пользовательский тип a.  Для энкодера определен только тип результата — Json.Encode.Value.  Рассмотрим примеры для типа UserData. Декодер для получения данных от пользователя: Функция Json.Decode.map3 принимает конструктора типа UserData. Далее передаются три декодера типа в соответствии с порядок их объявления в пользовательском типе UserData.  Функция decodeUserData может быть использована совместно с функциями Json.Decode.decodeString или Json.Decode.decodeValue. Пример использования из предыдущих статей. Функция encodeUserData производит кодирование пользовательского типа в тип Json.Encode.Value, который может быть отправлен наружу. По простому, Json.Encode.Value соответствует JSON объекту. Простые варианты описаны в документации, их можно изучить без особых трудностей. Давайте рассмотрим жизненные случаи, которые требуют некоторой ловкости пальцев. Предположим, у нас есть каталог товаров. И каждый товар может иметь произвольное количество атрибутов, каждый из которых имеет тип один из множества: JSON объект допустим следующего вида: Остальные возможные типы не будем рассматривать, работа с ними аналогична. Тогда пользовательский тип товар имел бы следующее описание: Слегка обсудим описанные типы. Есть товар (Product), который содержит список атрибутов/характеристик (Attributes). Каждый атрибут (Attribute) содержит идентификатор, наименование, размерность и значение. Значение атрибута описано как union type, по одному элементу для каждого типа значения характеристики. Тип Enum описывает одно значение из допустимого множества и содержит: идентификатор и человеко читаемое значение. Описание декодера, префикс Json.Decode опустим для краткости: Весь трюк содержится в функции decodeAttributeValue. При помощи функции Json.Decode.oneOf перебираются все допустимые декодеры для значения атрибута. В случае успешной распаковки одним из декодоров, значение тегируется соответствующим тегом из типа AttributeValue. Кодирование типа Product, может быть выполнено при помощи функции Json.Encode.object, в которую будут переданы закодированные атрибуты типа. Стоит уделить внимание кодированию типа AttributeValue. В соответствии с описанным ранее JSON объектом, энкодер может быть описан как, префикс Json.Encode опустим для краткости: Как видно, сопоставляем варианты типа и используем соответствующие энкодеры. Изменим описание атрибутов и определим их с использование дискриминатора типа. JSON объект атрибута, в этом случае, имел бы следующий вид: В данном случае дискриминатор типа хранится в поле type и определяет в каком поле хранится значение. Такая структура описания вероятно не самая удобная, но часто встречающаяся. Стоит ли менять описание типа для данного JSON объекта, наверное не стоит, лучше держать типы в удобной форме для внутреннего использования. В этом случае описание декодера может иметь следующий вид: В функции decodeAttribute2 сначала декодируем дискриминатор, в случае успеха — декодируем значение атрибута. Далее декодируем оставшиеся поля типа Attribute, а в качестве значения поля value указываем ранее полученное значение. Исходный код декодера. Встречаются случаи, когда API возвращает не весь объект, а лишь его часть. Например, при регистрации просмотра или смене статуса объекта. В этом случае в сообщении удобнее сразу получать обновленный объект, а все манипуляции скрыть за декодером. Для примера возьмем тот же товар, но добавим в него поле статус и будем обрабатывать запрос на закрытие товара. Или можно использовать функцию Json.Decode.map. Будем использовать функцию Date.fromString, которая реализована при помощи конструктора типа Date. Если в качестве представления даты/времени используется Timestamp, то декодер в общем виде можно описать как: Разработчик