dynjson

package module
v1.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2021 License: MIT Imports: 6 Imported by: 0

README

dynjson PkgGoDev Build Status Coverage Status Go Report Card

Client-customizable JSON formats for dynamic APIs.

Introduction

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.

Installation

go get github.com/cocoonspace/dynjson

Examples

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}]}

Limitations

  • 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.

Performance impact

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

Contribution guidelines

Contributions are welcome, as long as:

  • unit tests & comments are included,
  • no external package is used.

License

MIT - See LICENSE

Documentation

Overview

Package 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.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FieldsFromRequest added in v0.2.0

func FieldsFromRequest(r *http.Request, opt ...Option) []string

FieldsFromRequest returns the list of fields requested from a http.Request.

Without opt or with OptionMultipleFields, the expected format is: http://api.example.com/endpoint?select=foo&select=bar

With OptionCommaList, the expected format is: http://api.example.com/endpoint?select=foo,bar

Types

type Formatter

type Formatter struct {
	// contains filtered or unexported fields
}

Formatter is a dynamic API format formatter.

func NewFormatter

func NewFormatter() *Formatter

NewFormatter creates a new formatter.

func (*Formatter) Format

func (f *Formatter) Format(o interface{}, fields []string) (interface{}, error)

Format formats either a struct or a slice, returning only the selected fields (or all if none specified).

Example
var w http.ResponseWriter
var r *http.Request

type APIResult struct {
	Foo int    `json:"foo"`
	Bar string `json:"bar"`
}

f := NewFormatter()

res := &APIResult{Foo: 1, Bar: "bar"}
o, err := f.Format(res, FieldsFromRequest(r))
if err != nil {
	// handle error
}
err = json.NewEncoder(w).Encode(o) // {"foo": 1}
if err != nil {
	// handle error
}
Output:

type Option added in v0.2.0

type Option int

Option defines a FieldsFromRequest option.

const (
	// OptionMultipleFields expects multiple select query parameters.
	OptionMultipleFields Option = iota
	// OptionCommaList expects a single select parameter with comma separated values.
	OptionCommaList
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL