{
"Id": 1
}
誰がこんなもん欲しがるって言うんだよォー!!
クライアント側(JavaScriptとかだ)が明らかに困るだろ!
現状の確認
素直なコードとJSON
先のJSONは以下のようなコードで生成した。
package main
import (
"encoding/json"
"fmt"
)
type Sample struct {
Id int
}
func main() {
data := Sample{1}
bytes, err := json.Marshal(data)
if err != nil {
return
}
fmt.Println(string(bytes))
}
Go言語がわからない人でもきっとなんとなく読めるだろう。
素直だ。わかりやすい。このコードが"Id"
ではなく"id"
というkeyでJSON生成してくれれば何も問題はなかったのに。
少し書くのがめんどいコードとJSON
もちろん、出力されるkeyの名前を変えるくらい、わけない。
こうすればいいんだ。
type Sample struct {
Id int `json:"id"`
}
実に簡単。タグというメタデータを書く仕組みに則って書くだけだ。
もちろん、structのプロパティ全てに対して書く必要がある。
json.Marshal(data, &json.Option{LowerCamel:true}
みたいな書き方があるはずだ…!って?
僕の調べた限り、ない。
頑張って全てに書くんだ。
フィールド名変えたらタグの中身も変えるんだ。
いちいちやるんだ。全部やるんだ。
標準ライブラリ以外のJSONライブラリは?
僕がざっくり調べた限り、広く使われているものはないみたいだ。
色々なフレームワークが標準ライブラリのencoding/json
で動作することを期待している。
逃れることはできない…!
json.Marshaler
encoding/json
では、Marshalerというインタフェースを用意していて、そのメソッドを実装すれば挙動を自分で上書きできる。こんな感じ(ちなUnmarshalerもある)
package main
import (
"encoding/json"
"fmt"
)
type Sample struct {
Id int
}
func (s *Sample) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("{\"id\":%d}", s.Id)), nil
}
func main() {
data := &Sample{1}
bytes, err := json.Marshal(data)
if err != nil {
return
}
fmt.Println(string(bytes))
}
なので、自分でMarshalerを書くという逃げ道もあるだろう。
なお、jsonのstream encoder/decoder的なものは標準ライブラリには存在していない。
encoding/json
内部には持っているのだが、非公開なのだ。
JSR353みたいなの欲しい。
現実的な対策
泣きながら頑張ってタグ書く
たぶんみんなこうやってるんだろうな
json.Marshalerで頑張る
汎用的な変換ロジックを1つこさえて、JSONに変換する可能性があるたび、メソッド(的なの)を定義するのだ。
でもタグ情報が足りなくて上手く動かないコードありそう。
go generateを使う
いい感じのコードを生成するツールを作ればいんじゃね?
AST触りやすいGoさん偉いと思います go fmt もうまあじ
package main
import (
"encoding/json"
"fmt"
)
type Sample struct {
Id int
}
func main() {
data := &Sample{1}
bytes, err := json.Marshal(NewSampleJson(data))
if err != nil {
return
}
fmt.Println(string(bytes))
}
// ここから自動生成
func NewSampleJson(orig *Sample) *SampleJson {
ret := &SampleJson{}
ret.Id = orig.Id
return ret
}
type SampleJson struct {
Id int `json:"id"`
}
func (s *SampleJson) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("{\"id\":%d}", s.Id)), nil
}
フィールドのコピーを行うのが地味にダサい気がするんだけどどうなんだろう…。
あとは、ユーザが管理者か一般ユーザか非ログインかで出力するフィールドの制御とかも欲しいよね…。
以下みたいな感じで…。
type Person struct {
Id string
Name string
Email string
Himitsu string
}
func main() {
p := &Person{"vv", "vvakame", "vvakame@example.com", "猫が好き"}
b := NewSampleJsonBuilder()
b.AddAll()
b.Remove(b.Himitsu)
bytes, err := b.Marshal(p)
}
だいたいJsonPullParserだな!
実装例
この例だとomitemptyで""
とかも要素消えちゃうのでnilにしたい気がするけど*string
はなんか日常生活がダルそうだからなぁ…みたいな気持ち。そんなことない?
お願い
まだGo言語歴20時間くらいなので、なんかナイスな方法とか意見とかあったらコメントください。
お願いします!なんでもはしません!
追記 2015/06/25
jwgというライブラリを作って公開しました。