完成品はコチラ
ellie-app.com
guide.elm-lang.jp
🐊「受け取る値を厳密にするためにJSON.Decode.Value
使えって書いてるな…」
🐊「今回やりたいのは受け取るだけだしMaybe
だけやっちゃおw」
🐊「なんかここだけ厳密じゃないどまあ大丈夫っしょw」
そして時は流れ…
🐊「あっさり出来た!ツイートして終わりっ!」
🐊「ポチ!w」
flagsで読み込むとき JSON decoder慣れておくととても良いのでJSON decoder版も是非やってみたください!
— ABAB↑↓BA (@ababupdownba) 2020年9月3日
ガイドにもdecoder使った方が良いよって書いてあったはずです!
(楽するために見て見ぬフリしてました)
— 🐊すがわに🐊 (@nek0roll) 2020年9月3日
重要な要素っぽいので触れてみます(^o^)
AB先生に全部見抜かれてンだわ
ということでJSON Decoder
が気になったので触ります
JSON Decoderとは
JavaScriptの世界で使われる手法は、JSONを単にJavaScriptオブジェクトに変換し、何も間違いが起こらないよう祈るだけです。
しかし、もし何らかのタイプミスや想定外のデータに遭遇すると、あなたのコードのどこかで実行時例外が発生してしまいます。
コードの間違いなのか?それともデータの間違いなのか?その原因を探る作業の始まりです!
確かにサーバから来るJSON
は不確定で大変なイメージある
Elmの世界では、我々のプログラムにデータを取り込むより前にJSONを検証します。
もしデータに想定外の形式が含まれていれば、すぐにその点について気づくはずです。
おかしなデータが忍びこんで、忍び込んだところからちょっと離れた別の場所で実行時例外を発するような余地はありません。
これはJSONデコーダーによって達成されるのです。
あったら良いなが全部書いてあった。Elm
考えた人天才かな?
こんな事書かれちゃ試すしか無い!
JSON Decoder
を実装していく
今回来るJSON
key
自体がない時
null
null
である
key
はあるけどデータが空の時
[]
空の配列である
データがある時
[ { "id": 0, "todo": "todo1", "editing": false }, { "id": 1, "todo": "todo2", "editing": false }, ]
配列の中にobject
が入ったデータが来る
各TODOのDecoder
を定義する
id
idDecoder : Decoder Int idDecoder = D.field "id" D.int
id
フィールドはInt
であるべき事を定義
todo
todoFieldDecoder : Decoder String todoFieldDecoder = D.field "todo" D.string
todo
フィールドはString
であるべき事を定義
ここでTodo
型とtodo
の名前を被らせたことを後悔
ここだけField
と関数名に入っているのは後悔の証である
editting
edittingDecoder : Decoder Bool edittingDecoder = D.field "editting" D.bool
editting
はBool
であるべき事を定義
TODO自体のDecoder
を定義する
Todo
todoDecoder : Decoder Todo todoDecoder = D.map3 Todo idDecoder todoFieldDecoder edittingDecoder
type alias Todo = { id : Int , todo : String , editting : Bool }
この形になることを期待しTodo
型であることを定義
JSON.Decode.mapN
は要素ごとに増えていくらしい。わかりやすいネ
TODOリストのDecoder
を定義する
List Todo
todoListDecoder : Decoder (List Todo) todoListDecoder = D.list todoDecoder
List Todo
の形であるべき事を定義
Json.Decode.list
に作ったTodo
であることをチェックするtodoDecoder
を渡すだけ
実際にデコードする
main
main : Program D.Value Model Msg main = Browser.element { init = init , view = view , update = persistenceEvenryUpdate , subscriptions = \_ -> Sub.none }
(Maybe (List Todo))
で受けていた部分をJSON.Decode.Value
に変更
init
init : D.Value -> ( Model, Cmd Msg ) init todoList = ( { todoList = let result = D.decodeValue todoListDecoder todoList in case result of Ok decodedTodoList -> decodedTodoList Err _ -> [] , inputText = "" , errorMessage = NO_ERROR } , Cmd.none )
引数をJSON.Decode.Value
に変更
Just
orNothing
でパターンマッチしていた部分を
todoListDecoder
に変更しOk
orError
でパターンマッチに変更
これで出来上がりである
試しにlocalStorage.todoList
のjsonを壊してみると[]
で初期化された
エラーハンドリングもしたいけどそれは次回のお楽しみ
まとめ
- AB先生の言う通りめっちゃ面白かった
これは完全にレゴブロック
JSON
の期待する形を考えて、それにそって組み合わせていくだけだった
- エラーハンドリングもやりたい
Err
の時にとりあえず画面に出力するようにしたい