JSON Schema 便利ですよね。現在の規格は draft-07 で程良く枯れてきたように感じます。これを .NET で扱うライブラリにはいくつか候補があります。そのなかでも Manatee.Json を紹介したいと思います。
- Nuget NuGet Gallery | Manatee.Json 10.1.1
- GitHub gregsdennis/Manatee.Json: A fully object-oriented approach to JSON manipulation, validation, and serialization that focuses on modeling the JSON structure rather than mere string parsing and conversion.
- リファレンス Welcome!
特徴
JSON 関連で大抵のことはできるだけの機能が実装されています。その中でも特筆に値するのは以下の2点です。
Supports JSON Schema INCLUDED AND FREE!
- 良さげなソリューションがないから自分で書いた(意訳)
Json.Net の JSON Schema は nuget で簡単にインストールできるけど実は有料という…後から知って軽くトラブりました。有料でもいいんだけど取り回しにくい…
簡単に動かす例
せっかくなので F# を使ってみます。必要なファイルは Gist にも置いておきました。
まずは dotnet-sdk をなんらかの方法でインストールしてください。dotnet
コマンドを使って開発環境を作りましょう。
dotnet new console -lang F# -o sample
dotnet add package Manatee.Json
名前と数が入っているオブジェクトです。これをバリデーションしてみます。(item.json)
{
"name": "ペン",
"count": 3
}
対象には名前と数が必ず入っている、と JSON Schema で定義します。(schema.json)
{
"$schema": "http:\/\/json-schema.org\/draft-07\/schema#",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"count": {
"type": "integer"
}
},
"required": [
"name",
"count"
]
}
JSON Schema の条件に合致していたら Item クラスのインスタンスに値をセットして標準出力に表示、合っていなかったら例外を投げます。(Program.fs)
バリデーションが通らなかったときのエラーメッセージは適当です。Manatee.Json ver9 に慣れてしまってるので これ を見て検討必要かも。
// for Manatee.Json ver10
open System
open System.IO
open System.Linq
open Manatee.Json
open Manatee.Json.Schema
open Manatee.Json.Serialization
type Item() =
member val Name = "" with get, set
member val Count = 0 with get, set
[<EntryPoint>]
let main argv =
let serializer = JsonSerializer()
let schema =
File.ReadAllText (if argv.Length > 0 then argv.[0] else "schema.json")
|> JsonValue.Parse
|> serializer.Deserialize<JsonSchema>
let json =
File.ReadAllText (if argv.Length > 1 then argv.[1] else "item.json")
|> JsonValue.Parse
let result = schema.Validate json
if not (result.IsValid)
then
result.AdditionalInfo.Select (fun i -> i.Key + i.Value.ToString())
|> String.concat "\n"
|> System.Exception
|> raise
printfn "Schema OK"
let item = serializer.Deserialize<Item> json
printfn "Name: %s, Count: %d" item.Name item.Count
0
実行してみます。良さそうですね。
% dotnet run
Schema OK
Name: ペン, Count: 3
入力ファイルをいじってバリデーションを通らなくしてみます。下記は name
を削除して実行した結果です。これも良さそうですね。
% dotnet run
Unhandled Exception: System.Exception: missing["name"]
at Program.main(String[] argv) in /tmp/sample/Program.fs:line 30
落とし穴
上記の実行結果でめでたしめでたし、のように見えますよね。Json.Net に比べて知名度は高くないのにちゃんと動いた!やったー!といいたいところですが、では別の入力ファイルを用意してみましょう。
入力ファイル(item_escaped.json)
名前と数が入っているオブジェクト。但し、名前は Unicode エスケープされている。
{
"name": "\u30da\u30f3",
"count": 3
}
実行してみるとエラー…カタカナの範囲は U+30A0 - U+30FF で文字に問題はなさそうなんですが…
% dotnet run schema.json item_escaped.json
Unhandled Exception: Manatee.Json.JsonSyntaxException: Invalid UTF-32 code point. Path: '/name'
at Manatee.Json.Parsing.JsonParser.Parse(String source)
at Program.main(String[] argv) in /tmp/sample/Program.fs:line 23
どうやら Unicode エスケープされた文字をアンエスケープできない場合があるようです。利用者が少ないのか、そもそも気にされていないのかは分かりませんが結構な痛手です。
では修正 PR を作るか?それとも Unicode エスケープの処理を自前で書くか?いいえ、エラーメッセージの通り JSON Schema 以前に JSON の扱いで問題が起きています。JSON を扱えるライブラリはいくつかありますよね?
プロダクトコードで Manatee.Json を使ってるところは他の JSON ライブラリを使って文字をアンエスケープして、その結果を使ってバリデーションをしています。
この記事を見て Manatee.Json を使いたいと思った人はその辺りも含めて、使うかどうかを検討してみてください。
まとめ
- Manatee.Json いいよ
- F# 初めて書いて楽しかった(Emacs の fsharp-mode がデフォルトで十分使い易かった)
- 修正 PR 作りたいと思って早1年。とりあえず忘れないように記事化できて良かった