GleamとJSON
今回はGleamでJSONをエンコード・デコードする方法を解説します。
サンプルコードはこちらに上げてあります。
GleamのHTTPクライアントと組み合わせて気象庁のAPIを叩いているサンプルはこちらにあります。
ライブラリ
JSONを扱うライブラリはいくつかあります。
gleam_jsonはその1つです。
これは公式がサポートしているライブラリなのでまずはこれを使ってみると良いでしょう。
パーサーとしては公式ライブラリの他にjasperなどのライブラリがありますが、現時点(6/16)では標準ライブラリでlist.at
が削除された変更に追いつけておらず動きません。
gleam_json
インストール
gleam add gleam_json
以下のようにしてimportします。
import gleam/json
エンコード
json.to_string
を使います。この関数はJson
型をString
型に変換してくれます。
ここで以下のようなJSONを作ってみます。
{
"name": "arisu",
"age": 12,
"favorites": {
"food": "strawberry",
"hobby": "game"
}
}
エンコードを行うには、Json
型の値を作成する必要があります。
それらを行う関数として以下のものがあり、それらを組合せてJsonを表現します。
- array
- bool
- float
- int
- null
- nullable
- object
- preprocessed_array
- string
object([
#("name", string("arisu")),
#("age", int(12)),
#("favorites", object([
#("food", string("strawberry")),
#("hobby", string("game")),
])),
]) // => "{"name": "arisu","age": 12,"favorites": {"food": "strawberry","hobby": "game"}}"
preprocessed_array
とarray
の違いですが、preprocessed_array
は1つのリストに異なる型が入るのを許容し、array
は許容しないという違いがあります。
preprocessed_array([int(1), float(2.0), string("3")])
|> to_string // "[1, 2.0, \"3\"]"
array([1, 2, 3], of: int)
|> to_string // "[1, 2, 3]"
デコード
エンコードにあるJSONをデコードしてみます。
まず事前にデコードしたいJSONと同じ構造をした型を用意します。
この型はJSONに完全に対応する必要がありません。
というのも、引数として渡す関数はそれぞれ独立して実行されるからです。
従って必要な箇所の構造のみ定義することで型定義のコストを最小限にできます。
pub type Idol {
Idol(name: String, age: Int, favorites: Favorites)
}
pub type Favorites {
Favorites(hobby: String, food: String)
}
次に、json.decode
を使ってJSONをデコードします。
json.decode(
text, // JSONテキストデータ
dynamic.decode3(
Idol,
field(named: "name", of: string),
field(named: "age", of: int),
field( // 再帰的なフィールドはfieldの引数にfieldを指定すれば良い
named: "favorites",
of: dynamic.decode2(
Favorites,
field(named: "hobby", of: string),
field(named: "food", of: string),
),
),
),
)
デコードされるとOk(Idol("arisu", 12, Favorites("game", "strawberry")))
のように結果がResult
に包まれて返ってきます。
jasper
gleam_json
を使った方法を見ると分かりますが、この方法は型を定義したりとかなり面倒です。
型に則ってJSONを扱いたい場合は良いですが、APIを試しに叩いてみたい時は邪魔に感じることもあると思います。
そこで構造体を定義しなくてもJSONを使えるライブラリとしてjasperを紹介します。
なおjasperはJSONのパースのみ対応しています。
japserはgleam_jsonより直感的な記法でJSONから値を取り出せます。
import gleam/io
import jasper.{parse_json, query_json, String, Root, Key, Index}
pub fn main() {
let assert Ok(json) = parse_json("{ \"foo\": [1, true, \"hi\"] }")
let assert Ok(String(str)) = query_json(json, Root |> Key("foo") |> Index(2))
io.println(str)
}
READMEより引用。
先述のJSONからstrawberry
を取り出したい場合は、以下のように記述します。
{
"name": "arisu",
"age": 12,
"favorites": {
"food": "strawberry",
"hobby": "game"
}
}
query_json(json, Root |> Key("favorites") |> Key("food"))
// => Ok("strawberry")
より実用的な例
JSONを扱う処理というのは大抵の場合単体ではなく他の処理と組み合わせるケースが多いです。
そのような場合はどのように処理を記述していけば良いのか、自分の場合を混じえながら解説していきます。
JSONの処理はモジュールとして切りだす
JSONを扱う処理というのは大抵使い回されるケースが多いです。
そこで自分は再利用性を上げるためにそれらの処理をライブラリとして切り出すケースが多いです。
.
├── gleam.toml
├── manifest.toml
├── README.md
├── src
│ ├── myproj.gleam
│ └── schema
│ └── idol.gleam
└── test
└── myproj_test.gleam
4 directories, 6 files
pub type Idol {
Idol(name: String, age: Int)
}
/// JSON文字列をパースする関数
pub fn to_string() {
todo
}
/// 型をJSON文字列にエンコードする関数
pub fn from_string() {
todo
}
このモジュールはこんな感じで呼び出せます。
import schema/idol.{type Idol}
let arisu: Idol = idol.from_string("json文字列")
idol.to_string(Idol("haru", 12))
// => {"name": "haru", "age": 12}
まとめ
今回はGleamでJSONを扱う方法を紹介しました。
Webアプリを作る上でJSONを扱う機会は多いので、この記事が参考になったら幸いです。
Discussion