Elm Nyaman dan canggung. Json.Encoder dan Json.Decoder

Kami terus berbicara tentang Elm 0,18 .


Elm Nyaman dan canggung
Elm Nyaman dan canggung. Komposisi
Elm Nyaman dan canggung. Http, Tugas


Artikel ini akan membahas masalah encoders / decoder.


Decoder / encoders digunakan untuk:


  1. konversi respons dari sumber daya pihak ketiga (Http, WebSocket, dll.);
  2. interaksi melalui port. Saya akan memberi tahu Anda lebih banyak tentang port dan kode asli di artikel berikut.

Seperti yang dijelaskan sebelumnya, Elm mengharuskan kita untuk mengubah data eksternal menjadi tipe aplikasi internal. Modul Json.Decode bertanggung jawab untuk proses ini. Proses sebaliknya adalah Json.Encode .


Tipe yang mendefinisikan aturan decoding adalah Json.Decode.Decoder a . Tipe ini diparameterisasi oleh tipe pengguna dan menentukan cara mendapatkan tipe pengguna dari objek JSON.


Untuk pembuat enkode, hanya tipe hasil yang ditentukan - Json.Encode.Value .


Pertimbangkan contoh untuk tipe UserData.


type alias User = { id: Int , name: String , email: String } 

Dekoder untuk menerima data dari pengguna:


 decodeUserData : Json.Decode.Decoder UserData decodeUserData = Json.Decode.map3 UserData (Json.Decode.field “id” Json.Decode.int) (Json.Decode.field “name” Json.Decode.string) (Json.Decode.field “email” Json.Decode.string) encodeUserData : UserData -> Json.Encode.Value encodeUserData userData = Json.Encode.object [ ( “id”, Json.Encode.int userData.id) , ( “name”, Json.Encode.string userData.name) , ( “email”, Json.Encode.string userData.email) ] 

Fungsi Json.Decode.map3 menerima konstruktor tipe UserData. Selanjutnya, tiga decoder tipe ditransmisikan sesuai dengan urutan deklarasi mereka di tipe pengguna UserData.


Fungsi decodeUserData dapat digunakan bersama dengan fungsi Json.Decode.decodeString atau Json.Decode.decodeValue. Contoh penggunaan dari artikel sebelumnya.


Fungsi encodeUserData menghasilkan jenis pengkodean kustom tipe Json.Encode.Value, yang dapat dikirim. Secara sederhana, Json.Encode.Value sesuai dengan objek JSON.


Pilihan sederhana dijelaskan dalam dokumentasi, mereka dapat dipelajari tanpa banyak kesulitan. Mari kita lihat kasus kehidupan yang membutuhkan sentuhan tangan.


Decoder tipe serikat atau tipe diskriminator


Misalkan kita memiliki katalog barang. Dan setiap produk dapat memiliki sejumlah atribut yang berubah-ubah, yang masing-masing memiliki jenis satu dari banyak:


  1. bilangan bulat
  2. tali
  3. disebutkan. Mengasumsikan pilihan salah satu nilai yang valid.

Objek JSON valid sebagai berikut:


 { “id”: 1, “name”: “Product name”, “price”: 1000, “attributes”: [ { “id”: 1, “name”: “Length”, “unit”: “meters”, “value”: 100 }, { “id”: 1, “name”: “Color”, “unit”: “”, “value”: { “id”: 1, “label”: “red” } },... ] } 

Jenis yang tersisa mungkin tidak akan dipertimbangkan, bekerja dengan mereka serupa. Maka jenis produk khusus akan memiliki uraian berikut:


 type alias Product = { id: Int , name: String , price: Int , attributes: Attributes } type alias Attributes = List Attribute type alias Attribute = { id: Int , name: String , unit: String , value: AttributeValue } type AttributeValue = IntValue Int | StringValue String | EnumValue Enum type alias Enum = { id: Int , label: String } 

Kami akan sedikit membahas jenis yang dijelaskan. Ada produk (Produk), yang berisi daftar atribut / karakteristik (Atribut). Setiap atribut (Atribut) berisi pengidentifikasi, nama, dimensi, dan nilai. Nilai atribut digambarkan sebagai tipe gabungan, satu elemen untuk setiap jenis nilai karakteristik. Tipe Enum menggambarkan satu nilai dari set yang dapat diterima dan berisi: pengidentifikasi dan nilai yang dapat dibaca manusia.


Deskripsi dekoder, abaikan awalan Json.Decode untuk singkatnya:


 decodeProduct : Decoder Product decodeProduct = map4 Product (field “id” int) (field “name” string) (field “price” int) (field “attributes” decodeAttributes) decodeAttributes : Decoder Attributes decodeAttributes = list decodeAttribute decodeAttribute : Decoder Attribute decodeAttribute = map4 Attribute (field “id” int) (field “name” string) (field “unit” string) (field “value” decodeAttributeValue) decodeAttributeValue : Decoder AttributeValue decodeAttributeValue = oneOf [ map IntValue int , map StringValue string , map EnumValue decodeEnumValue ] decodeEnumValue : Decoder Enum decodeEnumValue = map2 Enum (field “id” int) (field “label” string) 

Seluruh trik terkandung dalam fungsi decodeAttributeValue. Menggunakan fungsi Json.Decode.oneOf iterates atas semua decoder yang valid untuk nilai atribut. Dalam kasus yang berhasil dibongkar oleh salah satu decoder, nilainya ditandai dengan tag yang sesuai dari jenis AttributeValue.


Pengkodean jenis produk dapat dilakukan menggunakan fungsi Json.Encode.object, di mana atribut tipe yang dikodekan akan diteruskan. Perlu memperhatikan pengkodean tipe AttributeValue. Sesuai dengan objek JSON yang dijelaskan sebelumnya, encoder dapat digambarkan sebagai awalan Json.Encode dihilangkan karena singkatnya:


 encodeAttributeValue : AttributeValue -> Value encodeAttributeValue attributeValue = case attributeValue of IntValue value -> int value StringValue value -> string value EnumValue value -> object [ (“id”, int value.id) , (“id”, string value.label) ] 

Seperti yang Anda lihat, kami membandingkan opsi jenis dan menggunakan pembuat enkode yang sesuai.


Ubah deskripsi atribut dan tentukan dengan menggunakan pembeda tipe. Objek atribut JSON, dalam hal ini, akan terlihat seperti ini:


 { “id”: 1, “name”: “Attribute name”, “type”: “int”, “value_int”: 1, “value_string”: null, “value_enum_id”: null, “value_enum_label”: null } 

Dalam kasus ini, pembeda tipe disimpan di bidang tipe dan menentukan di bidang mana nilai disimpan. Struktur deskripsi seperti itu mungkin bukan yang paling nyaman, tetapi sering ditemukan. Mungkin tidak layak untuk mengubah deskripsi jenis untuk objek JSON ini, lebih baik menyimpan jenisnya dalam bentuk yang nyaman untuk penggunaan internal. Dalam hal ini, deskripsi dekoder mungkin sebagai berikut:


 decodeAttribute2 : Decoder Attribute decodeAttribute2 = field "type" string |> andThen decodeAttributeValueType |> andThen (\attributeValue -> map4 Attribute (field "id" int) (field "name" string) (field "unit" string) (succeed attributeValue) ) decodeAttributeValueType : String -> Decoder AttributeValue decodeAttributeValueType valueType = case valueType of "int" -> field "value_int" int |> Json.Decode.map IntValue "string" -> field "value_string" string |> Json.Decode.map StringValue "enum" -> map2 Enum (field "value_enum_id" int) (field "value_enum_label" string) |> Json.Decode.map EnumValue _ -> Json.Decode.fail "Unknown attribute type" 

Dalam fungsi decodeAttribute2, pertama-tama kita mendekode pembeda, jika berhasil, kita mendekode nilai atribut. Selanjutnya, kami mendekode bidang yang tersisa dari jenis atribut, dan menentukan nilai yang diperoleh sebelumnya sebagai nilai bidang nilai.


Kode sumber decoder .


Pembaruan Jenis Parsial


Ada beberapa kasus ketika API tidak mengembalikan seluruh objek, tetapi hanya sebagian saja. Misalnya, ketika mendaftar untuk melihat atau mengubah status suatu objek. Dalam hal ini, akan lebih mudah untuk segera menerima objek yang diperbarui dalam pesan, dan menyembunyikan semua manipulasi di belakang dekoder.


Misalnya, kami mengambil produk yang sama, tetapi menambahkan bidang status ke sana dan akan memproses permintaan untuk menutup produk.


 type alias Product = { id: Int , name: String , price: Int , attributes: Attributes , status: Int } decodeUpdateStatus : Product -> Decoder Product decodeUpdateStatus product = field “status” int |> andThen (\newStatus -> succeed { product | status = newStatus} ) 

Atau Anda dapat menggunakan fungsi Json.Decode.map.


 decodeUpdateStatus : Product -> Decoder Product decodeUpdateStatus product = field “status” int |> map (\newStatus -> { product | status = newStatus} ) 

Tanggal dan waktu


Kami akan menggunakan fungsi Date.fromString, yang diimplementasikan menggunakan konstruktor dari tipe Date.


 decodeDateFromString : Decoder Date.Date decodeDateFromString = string |> andThen (\stringDate -> case Date.fromString stringDate of Ok date -> Json.Decode.succeed date Err reason -> Json.Decode.fail reason ) 

Jika Timestamp digunakan sebagai representasi tanggal / waktu, maka dekoder secara umum dapat digambarkan sebagai:


 decodeDateFromTimestamp : Decoder Date.Date decodeDateFromTimestamp = oneOf [ int |> Json.Decode.map toFloat , float ] |> Json.Decode.map Date.fromTime 

Source: https://habr.com/ru/post/id424437/


All Articles