Goでjsonパーサーを自作する
jsonのパーサーをgoで作成したので共有したいと思います。
jsonの字句解析
jsonの字句解析で行っていることを解説していきます。
{"example": "text"}
例としてこのようなJSONがあったとします。
これを前から見ていき、条件によってトークンに分割していきます。
["{", "example", ":", "text", "}"]
結果はこのような感じになります。
実際の実装では型でトークンを作成しています。
考え方
{"example": "text"}
↑
カーソルを用意し前から順番に見ていきます。
最初が{であればオブジェクトなので{を配列に格納し次に}が来るまでカーソルを進めます
{"example": "text"}
↑
"が取得された場合次の"まではstringなのでこのjsonの場合exampleを配列に格納します
{"example": "text"}
↑
:が取得された場合はそのまま配列に格納します
"text"に関しては一つ前のものと同様です
{"example": "text"}
↑
最後に}を配列に格納すれば字句解析の完了です
jsonのvalueがstring以外だった場合
{"example": 12}
↑
このような場合はnumberとして扱う文字列を定義しておき、それにマッチするものはnumberとして扱います
numberとして扱うものは配列に格納時にInt型, Float型に変換しておきます
{"example": true}
↑
またboolだった場合も同様にtrueとfalseにマッチするものはboolとして扱います
こちらも配列に格納時にBool型に変換しておきます
jsonのパーサー
["{", "example", ":", "text", "}"]
このようなトークン列を[]map[string]interface{}にします。
map[example: text]
このような結果が得られます。
考え方
["{", "example", ":", "text", "}"]
↑
カーソルを用意し前から順番に見ていきます。
最初が{だった場合はオブジェクトなのでmapを用意して次に}が来るまでカーソルを動かします。
["{", "example", ":", "text", "}"]
↑
exampleは:の後の値ではないためjsonのkeyであることがわかります。なのでmapのkeyとして設定します。
["{", "example", ":", "text", "}"]
↑
textは:のあとに来ておりvalueであることが確定しexampleのvalueとして格納します。
["{", "example", ":", "text", "}"]
↑
最後にこれ以上配列に何も無いことを確認し、処理が終了となります。
jsonのvalueが配列かオブジェクトだった場合
["{", "example", ":", "{", "example2", ":", "text", "}"]
↑
このようになっていた場合再度{を取得したタイミングで上記の一番最初の処理に戻り、オブジェクトとしてパースしていきます。
["{", "example", ":", "[", 12, "text", "]"]
↑
このような場合次の]が来るまで前から順番に配列に格納しmapのvalueに配列を格納します。
字句解析時点で型が変更されているため特別な処理必要としていません。
リポジトリ
実際の実装はこちらです。
Discussion