Мне нужно прочитать из RSS-канала, который содержит элемент ‹enclosure› в Elm.
Хотя я могу удобно использовать YQL, чтобы напрямую получить JSON-версию
фида, они бывают разных форм. :
//case 1: As object { ... enclosure: { url: 'http://...' , length:12345, type:'audio/mpeg' } ... } //case 2: As list, I'll take the first one { ... enclosure : [ { url:'http://...', length:12345, type:'audio/mpeg'} , { url:'http://...', length:12345, type:'audio/mpeg'} , ... ] } //case 3: enclosure with type other than audio will be filtered out { ... enclosure: { url: 'http://...' , length:12345, type:'video/mp4' } ... } //case 4: no enclosure, this will be filtered out too { ... }
Чтобы декодировать его в такой тип:
type alias Model = { list : List Item } type alias Item = { url : String }
Мы можем сделать это один за другим и сочинить вместе. Сначала мы делаем простой случай, просто декодируем URL-адрес и вводим его как кортеж для дальнейшей обработки.
import Json.Decode as Json decodeSingleEnclosure : Json.Decoder (String, String) decodeSingleEnclosure = Json.object2 (,) ("url" := Json.string) ("type" := Json.string)
Нам нужно выполнить проверку этого типа корпуса, как в случае 3. Для этого мы будем использовать andThen.
decodeSingleEnclosureUrl : Json.Decoder String decodeSingleEnclosureUrl = decodeSingleEnclosure `Json.andThen` (\(url, type_) -> if Regex.contains (Regex.regex "^audio/") type_ then Json.succeed url else Json.fail "not audio type" )
Чтобы декодировать список вложений в случае 2, мы сначала декодируем его как список, а затем берем первый элемент. Обратите внимание, что мы используем may, чтобы разрешить неудачное декодирование в списке и отфильтровать его с помощью filterMap.
decodeEnclosureListUrl : Json.Decoder String decodeEnclosureListUrl = Json.list (Json.maybe decodeSingleEnclosureUrl) `Json.andThen` (\list -> -- List (Maybe String) let head = list |> List.filterMap identity |> List.head in case head of Just first -> Json.succeed first Nothing -> Json.fail "cannot get enclosure" )
Теперь мы можем использовать oneOf для решения как случая 1, так и случая 2.
decodeEnclosureUrl : Json.Decoder String decodeEnclosureUrl = Json.oneOf [ "enclosure" := decodeSingleEnclosureUrl , "enclosure" := decodeEnclosureListUrl ]
Наконец, мы разделяем этот URL-адрес на элемент списка. Если URL не найден, элемент пропускается
decodeItem: Json.Decoder (Maybe Item) decodeItem = Json.maybe (Json.object1 Item decodeEnclosureUrl) decodeModel : Json.Decoder Model decodeModel = Json.map Model ( ("items" := Json.list decodeItem) `Json.andThen` (\list -> list |> List.filterMap identity |> Json.succeed ) )
Проверьте весь пример в этом суть. Вы можете скопировать его на elm-lang.org/try, чтобы запустить его.
Декодирование сложного JSON в Elm поначалу показалось мне довольно трудным. Мне нужно обернуть голову и ознакомиться со всей концепцией декодера. Опять же, композиция функций является основным ключом к работе со сложным JSON. Постарайтесь сначала прийти к самому простому случаю, а затем постепенно наращивайте его.
Теги: elm, json, программирование
через 網絡暴民 Блог Джеки http://ift.tt/2dHxyO8