Client-customizable JSON formats for dynamic APIs.
dynjson allow APIs to return only fields selected by the API client:
GET https://api.example.com/v1/foos
[{"id":1,foo":1,"bar":2,"baz":3}]
GET https://api.example.com/v1/foos?select=foo
[{"foo":1}]
GET https://api.example.com/v1/foos/1?select=foo
{"foo":1}
dynjson mimicks the original struct using the original types and json tags. The field order is the same as the select parameters.
go get github.com/cocoonspace/dynjson
type APIResult struct {
Foo int `json:"foo"`
Bar string `json:"bar"`
}
f := dynjson.NewFormatter()
res := &APIResult{Foo:1, Bar:"bar"}
o, err := f.Format(res, dynjson.FieldsFromRequest(r))
if err != nil {
// handle error
}
err = json.NewEncoder(w).Encode(o) // {"foo": 1}
With struct fields:
type APIResult struct {
Foo int `json:"foo"`
Bar APIIncluded `json:"bar"`
}
type APIIncluded struct {
BarFoo int `json:"barfoo"`
BarBar string `json:"barbar"`
}
f := dynjson.NewFormatter()
res := &APIResult{Foo: 1, Bar: APIIncluded{BarFoo:1, BarBar: "bar"}}
o, err := f.Format(res, []string{"foo", "bar.barfoo"})
if err != nil {
// handle error
}
err = json.NewEncoder(w).Encode(o) // {"foo": 1, "bar":{"barfoo": 1}}
With slices:
type APIResult struct {
Foo int `json:"foo"`
Bar string `json:"bar"`
}
f := dynjson.NewFormatter()
res := []APIResult{{Foo: 1, Bar: "bar"}}
o, err := f.Format(res, []string{"foo"})
if err != nil {
// handle error
}
err = json.NewEncoder(w).Encode(o) // [{"foo": 1}]
type APIResult struct {
Foo int `json:"foo"`
Bar []APIItem `json:"bar"`
}
type APIItem struct {
BarFoo int `json:"barfoo"`
BarBar string `json:"barbar"`
}
f := dynjson.NewFormatter()
res := &APIResult{Foo: 1, Bar: []APIItem{{BarFoo: 1, BarBar: "bar"}}}
o, err := f.Format(res, []string{"foo", "bar.barfoo"})
if err != nil {
// handle error
}
err = json.NewEncoder(w).Encode(o) // {"foo": 1, "bar":[{"barfoo": 1}]}
- Anonymous fields without a json tag (embedded by the Go JSON encoder in the enclosing struct) are not supported,
- Maps are copied as is, you cannot filter map contents using
map_field_name.map_key
.
BenchmarkFormat_Fields
BenchmarkFormat_Fields-8 2466639 480 ns/op 184 B/op 7 allocs/op
BenchmarkFormat_NoFields
BenchmarkFormat_NoFields-8 5255031 232 ns/op 32 B/op 1 allocs/op
BenchmarkRawJSON
BenchmarkRawJSON-8 5351313 223 ns/op 32 B/op 1 allocs/op
Contributions are welcome, as long as:
- unit tests & comments are included,
- no external package is used.
MIT - See LICENSE