ADVENT CALENDAR 2019

ほんの10行でJsonとかXmlとか楽々読み込み with F#

By wraikny

はじめに

JsonXmlHtmlCSVを読み込みたいことってありますよね。 でも文字列でアクセスするのはバグの要因になるし、かといってすべてClassなどにバインディングするコードを書くのは大変です。

urlやファイルパスを指定しただけで型が自動生成されて、すぐさまコード補完が効き始めたら、嬉しくありませんか?

今日はF#公式ライブラリFSharp.Dataの紹介をします。

環境

.NET Core SDK 3.0以上が必要です。
未インストールの方はこちらから
https://dotnet.microsoft.com/download

$ dotnet --version
3.0.100

また、VSCodeやVimを使っている方は、Ionideという拡張機能をインストールすることで補完等が効くようになります。 自分はVSCode + Ionideを使っていてオススメです。

テンプレート

を用意したので、こちらにあるのでCloneなりUse this templateしてください。
https://github.com/wraikny/FSharp.Data-Template

ローカルで開いたら、初回のみrestore(復元)を実行し、

dotnet tool restore
dotnet paket restore

以降は以下のコマンドで実行できます。

dotnet fsi --exec src/Main.fsx

書いてみる

src/Main.fsx を開くと、

printfn "Hello, world"

とあるはずです。

ではGithubのapiから、FSharp.Dataのリポジトリのissue一覧を取得して表示するコードを書いてみましょう。 ファイルの中身は一度消去してください。

なお、F#のコードは上から下に一方向にしか書いて行けないので、ここに記載する順番にコードを書くものとします。

まずFSharp.Dataを使用するために以下のように記述して、間接的にdllを参照します。

#load "./FSharp.Data.fsx"

そして、FSharp.DataJsonProviderを使って型生成を行いましょう。 APIのurlを指定してGitHub型としてエイリアスを作ります。

type GitHub = FSharp.Data.JsonProvider<"https://api.github.com/repos/fsharp/FSharp.Data/issues">

Ionideを入れていれば、自動的に型生成されて補完が効き始めるはずです。 (数秒のラグはあるかもしれません)

では、読み込んでいきましょう。

GitHub.GetSamples()
|> Seq.filter(fun x -> x.)

コード補完が働いているのがわかると思います!

(表示されなかったら一度.を削除してもう一度打ってみるといいかもしれない)

続きを書いていきます。

Stateopenのものだけを取り出し、そこから5個だけ取り出すコードです。

GitHub.GetSamples()
|> Seq.filter (fun x -> x.State = "open")
|> Seq.truncate 5

F#ではイコールが=です。==ではないです。

|>はパイプ演算子と言って、関数を順次適用していくものです。 見た目はshellのパイプやC#のメソッドチェーンに近いですね。 ここではLinqに相当する操作をしています。

続いて、得られた値を整形していきましょう。

|> Seq.map(fun x -> sprintf "#%d: %s" x.Number x.Title)
|> String.concat "\n"
|> printfn "%s"

mapはmapping、つまり写像です。 sprintfを使って入力を文字列に変換しています。

String.concatは文字列を結合する関数です。

最後にprintfn "%s"にパイプすることで、文字列を出力します。

では、実行してみましょう! おおよそ以下のように出力されれば大丈夫です。

$ dotnet fsi --exec src/Main.fsx
#1295: Error with embedded resource in XmlProvider
#1294: Any way to get siblings of HtmlNode?
#1293: Http Utilities - Encoding problem
#1292: Can't get inner text of a script
#1291: Expose InnerResponse, Http properties

タイプミスなどは優しくエラーを出してくれるので、その場合は言われた場所をよく確認しましょう。

ここまでで、全体像は以下のようになっているはずです。

Main.fsx

#load "./FSharp.Data.fsx"

type GitHub = FSharp.Data.JsonProvider<"https://api.github.com/repos/fsharp/FSharp.Data/issues">

GitHub.GetSamples()
|> Seq.filter (fun x -> x.State = "open")
|> Seq.truncate 5
|> Seq.map(fun x -> sprintf "#%d: %s" x.Number x.Title)
|> String.concat "\n"
|> printfn "%s"

空行除けば8行しかありませんね! お手軽すぎる!!

また、こことは別のurlを読み込みたいこともあります。 処理を関数にして、Loadメソッドを使用して動的に取得してみましょう。

#load "./FSharp.Data.fsx"

type GitHub = FSharp.Data.JsonProvider<"https://api.github.com/repos/fsharp/FSharp.Data/issues">
let printIssues n (x : GitHub.Root []) =
  x |> Seq.filter (fun x -> x.State = "open")
    |> Seq.truncate n
    |> Seq.map(fun x -> sprintf "#%d: %s" x.Number x.Title)
    |> String.concat "\n"
    |> printfn "%s"

GitHub.GetSamples() |> printIssues 3
printfn ""

GitHub.Load("https://api.github.com/repos/AmusementCreators/WebSite/issues")
|> printIssues 7

printfn ""

これでもたったの13行です!

実行すると、このように得られます!

$ dotnet fsi --exec src/Sample.fsx
#1295: Error with embedded resource in XmlProvider
#1294: Any way to get siblings of HtmlNode?
#1293: Http Utilities - Encoding problem

#77: ヘッダー画像のサイズが共有時に良くない(サイズと見栄え)
#76: Articles, Postsの一覧にアドカレの記事が表示されない。
#70: Hugo アップデート
#69: meta name="keywords" タグがない
#66: AmCr アドベントカレンダー 2019
#57: タイトル(と本文)をゴシック体にする
#45: 著者ページの実装

ここまで書いたコードは Smaple.fsx として載せてあります。

まとめ

今回はAPIからURL経由でJsonを取得させましたが、urlを入れた箇所にそのままファイルパスを指定することで読み込むことも可能ですし、

type Hoge = JsonProvider<"""
{
    hoge: "neko",
    fuga: "inu"
}
""">

のように直接サンプルを書くこともできるなど、とてもシンプルで使い勝手が良いです。

FSharp.Data には、今回紹介したJsonProvider以外にもXml、Html、CSVのType Providerが実装されています。

FSharp.Data以外でも YamlConfigProviderSQLProviderExcelProvider など、様々なType Providerライブラリがあります。

スクリプトでお手軽にAPIやファイルを読み込めるF#とFSharp.Dataをぜひ使ってみてください!

今回のリポジトリをもう一度貼っておきます。
⭐などいただけると嬉しいです。
https://github.com/wraikny/FSharp.Data-Template

SHARE THIS POST