json

package module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: May 26, 2022 License: Apache-2.0 Imports: 13 Imported by: 6

Documentation

Overview

Package json contains interface specifications for representing any Go type as JSON where possible. Using the goprintasjson tool allows for quick code generation of scaffolding to make any Go type easily used as JSON.

Index

Examples

Constants

This section is empty.

Variables

Client provides a way to change the default HTTP client for any further package HTTP request function calls. By default, it is set to http.DefaultClient. This is particularly useful when creating mockups and other testing.

View Source
var TimeOut int = 60

TimeOut is a package global timeout for any of the high-level https query functions in this package. The default value is 60 seconds.

Functions

func Escape

func Escape(in string) string

specification (unlike the encoding/json standard which defaults to escaping many other characters as well unnecessarily).

Example
package main

import (
	"github.com/rwxrob/fn"
	json "github.com/rwxrob/json"
)

func main() {
	set := fn.A[string]{
		`<`, `>`, `&`, `"`, `'`,
		"\t", "\b", "\f", "\n", "\r",
		"\\", "\"", "💢", "д",
	}
	set.Map(json.Escape).Print()
}
Output:

<>&\"'\t\b\f\n\r\\\"💢д

func Fetch added in v0.6.0

func Fetch(it *Request) error

Fetch passes the Request Client and unmarshals the JSON response into the data struct passed by pointer. Only new data will be unmarshaled leaving any existing data alone.

All output data is expected to be JSON with the appropriate headers added to indicate it.

If a Body is sent, it will be encoded as if submit from a POST form.

Fetch observes the package global json.TimeOut.

Status codes not in th 200s range will return an error with the status message.

The http.DefaultClient is used by default but can be changed by setting json.Client.

Example
package main

import (
	"fmt"
	_http "net/http"

	ht "net/http/httptest"

	json "github.com/rwxrob/json"
)

func main() {

	// serve get
	handler := _http.HandlerFunc(
		func(w _http.ResponseWriter, r *_http.Request) {
			fmt.Fprintf(w, `{"get":"t"}`)
		})
	svr := ht.NewServer(handler)
	defer svr.Close()

	// serve get int
	handler0 := _http.HandlerFunc(
		func(w _http.ResponseWriter, r *_http.Request) {
			fmt.Fprintf(w, `20220322075441`)
		})
	svr0 := ht.NewServer(handler0)
	defer svr0.Close()

	// serve post
	handler1 := _http.HandlerFunc(
		func(w _http.ResponseWriter, r *_http.Request) {
			fmt.Fprintf(w, `{"post":"t","c":"t"}`)
		})
	svr1 := ht.NewServer(handler1)
	defer svr1.Close()

	// serve put
	handler2 := _http.HandlerFunc(
		func(w _http.ResponseWriter, r *_http.Request) {
			fmt.Fprintf(w, `{"put":"t"}`)
		})
	svr2 := ht.NewServer(handler2)
	defer svr2.Close()

	// serve patch
	handler3 := _http.HandlerFunc(
		func(w _http.ResponseWriter, r *_http.Request) {
			fmt.Fprintf(w, `{"patch":"t"}`)
		})
	svr3 := ht.NewServer(handler3)
	defer svr3.Close()

	// serve delete
	handler4 := _http.HandlerFunc(
		func(w _http.ResponseWriter, r *_http.Request) {
			fmt.Fprintf(w, `{"delete":"t"}`)
		})
	svr4 := ht.NewServer(handler4)
	defer svr4.Close()

	json.TimeOut = 4

	// create the struct type matching the REST query JSON
	type Data struct {
		Get     string `json:"get"`
		Post    string `json:"post"`
		Put     string `json:"put"`
		Patch   string `json:"patch"`
		Delete  string `json:"delete"`
		Changed string `json:"c"`
		Ignored string `json:"i"`
	}

	data := &Data{
		Changed: "o",
		Ignored: "i",
	}
	jsdata := json.This{data}
	jsdata.Print()

	req := &json.Request{URL: svr.URL, Into: data}

	if err := json.Fetch(req); err != nil {
		fmt.Println(err)
	}
	jsdata.Print()

	anint := 0
	req = &json.Request{URL: svr0.URL, Into: &anint}
	if err := json.Fetch(req); err != nil {
		fmt.Println(err)
	}
	fmt.Println(anint)

	req = &json.Request{Method: `POST`, URL: svr1.URL, Into: data}
	if err := json.Fetch(req); err != nil {
		fmt.Println(err)
	}
	jsdata.Print()

	req = &json.Request{Method: `PUT`, URL: svr2.URL, Into: data}
	if err := json.Fetch(req); err != nil {
		fmt.Println(err)
	}
	jsdata.Print()

	req = &json.Request{Method: `PATCH`, URL: svr3.URL, Into: data}
	if err := json.Fetch(req); err != nil {
		fmt.Println(err)
	}
	jsdata.Print()

	req = &json.Request{Method: `DELETE`, URL: svr4.URL, Into: data}
	if err := json.Fetch(req); err != nil {
		fmt.Println(err)
	}
	jsdata.Print()

}
Output:

{"get":"","post":"","put":"","patch":"","delete":"","c":"o","i":"i"}
{"get":"t","post":"","put":"","patch":"","delete":"","c":"o","i":"i"}
20220322075441
{"get":"t","post":"t","put":"","patch":"","delete":"","c":"t","i":"i"}
{"get":"t","post":"t","put":"t","patch":"","delete":"","c":"t","i":"i"}
{"get":"t","post":"t","put":"t","patch":"t","delete":"","c":"t","i":"i"}
{"get":"t","post":"t","put":"t","patch":"t","delete":"t","c":"t","i":"i"}

func Marshal added in v0.4.0

func Marshal(v any) ([]byte, error)

Marshal mimics json.Marshal from the encoding/json package without the broken, unnecessary HTML escapes and extraneous newline that the json.Encoder adds. Call this from your own MarshalJSON methods to get JSON rendering that is more readable and compliant with the JSON specification (unless you are using the extremely rare case of dumping that into HTML, for some reason). Note that this cannot be called from any structs MarshalJSON method on itself because it will cause infinite functional recursion. Write a proper MarshalJSON method or create a dummy struct and call json.Marshal on that instead.

Example
package main

import (
	stdjson "encoding/json"
	"fmt"

	json "github.com/rwxrob/json"
)

func main() {
	m := map[string]string{"<foo>": "&bar"}

	// the good way
	buf, err := json.Marshal(m)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(buf))

	// the broken encoding/json way
	buf, err = stdjson.Marshal(m)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(buf))

}
Output:

{"<foo>":"&bar"}
{"\u003cfoo\u003e":"\u0026bar"}

func MarshalIndent added in v0.4.0

func MarshalIndent(v any, a, b string) ([]byte, error)

MarshalIndent mimics json.Marshal from the encoding/json package but without the escapes, etc. See Marshal.

Example
package main

import (
	stdjson "encoding/json"
	"fmt"

	json "github.com/rwxrob/json"
)

func main() {
	m := map[string]string{"<foo>": "&bar"}

	// the good way
	buf, err := json.MarshalIndent(m, " ", " ")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(buf))

	// the broken encoding/json way
	buf, err = stdjson.MarshalIndent(m, " ", " ")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(buf))

}
Output:

{
  "<foo>": "&bar"
 }
{
  "\u003cfoo\u003e": "\u0026bar"
 }

func Unmarshal added in v0.4.0

func Unmarshal(buf []byte, v any) error

Unmarshal mimics json.Unmarshal from the encoding/json package.

Example
package main

import (
	"fmt"

	json "github.com/rwxrob/json"
)

func main() {
	m := new(map[string]string)
	if err := json.Unmarshal([]byte(`{"<foo>":"&bar"}`), m); err != nil {
		fmt.Println(err)
	}
	fmt.Println(m)
	if err := json.Unmarshal([]byte(`{"<foo>":"&bar"}`), m); err != nil {
		fmt.Println(err)
	}
	fmt.Println(m)
}
Output:

&map[<foo>:&bar]
&map[<foo>:&bar]

Types

type AsJSON added in v0.1.1

type AsJSON interface {
	JSON() ([]byte, error)
	String() string
	Print()
	Log() string
	MarshalJSON() ([]byte, error)
	UnmarshalJSON(buf []byte) error
}

AsJSON specifies a type that must support marshaling using the rwxrob/json package with its defaults for marshaling and unmarshaling which do not have unnecessary escaping.

String is from fmt.Stringer, but fulfilling this interface in this package promises to render the string specifically using rwxrob/json default output marshaling --- especially when it comes to consistent indentation, wrapping, and escaping. While JSON is a flexible format, consistency ensures the most efficient and sustainable creation of tests and other systems that require such consistency, whether or not dependency on such consistency is a "good idea".

Printer specifies methods for printing self as JSON and will log any error if encountered. Printer provides a consistent representation of any structure such that it an easily be read and compared as JSON whenever printed and test. Sadly, the default string representations for most types in Go are virtually unusable for consistent representations of any structure. And while it is true that JSON data should be supported in any way that is it presented, some consistent output makes for more consistent debugging, documentation, and testing.

AsJSON implementations must Print and Log the output of String from the same interface.

MarshalJSON and UnmarshalJSON must be explicitly defined and use the rwxrob/json package to avoid confusion. Use of the helper json.This struct may facilitate this for existing types that do not wish to implement the full interface.

type Request added in v0.6.0

type Request struct {
	Method string            // GET, POST, etc. (will upper)
	URL    string            // base url with no query string
	Query  url.Values        // query string to append to URL
	Header map[string]string // never more than one of same
	Body   url.Values        // body data, will JSON encode
	Into   any               // pointer to struct for unmarshaling
}

Request is a human-friendly way to think of web requests and the resulting JSON unmarshaled response. This design more similar to a pragmatic curl request than the canonical specification (unique headers, for example).

Note that passing the query string as url.Values automatically add a question mark (?) followed by the URL encoded values to the end of the URL which may present a problem if the URL already has a query string. Encouraging the use of url.Values for passing the query string serves as a reminder that all query strings should be URL encoded (as is often forgotten).

type This added in v0.5.0

type This struct{ This any }

This encapsulates anything with the AsJSON interface from this package by simply assigning a new variable with that item as the only value in the structure:

something := []string{"some","thing"}
jsonified := json.This{something}
jsonified.Print()
Example (Bool)
package main

import (
	json "github.com/rwxrob/json"
)

func main() {
	this := json.This{true}
	this.Print()
}
Output:

true
Example (Map)
package main

import (
	json "github.com/rwxrob/json"
)

func main() {
	this := json.This{map[string]string{"foo": "bar"}}
	this.Print()
}
Output:

{"foo":"bar"}
Example (Nil)
package main

import (
	json "github.com/rwxrob/json"
)

func main() {
	this := json.This{nil}
	this.Print()
}
Output:

null
Example (Numbers)
package main

import (
	json "github.com/rwxrob/json"
)

func main() {
	this := json.This{23434}
	this.Print()
	this.This = -24.24234
	this.Print()
}
Output:

23434
-24.24234
Example (Slice)
package main

import (
	json "github.com/rwxrob/json"
)

func main() {
	this := json.This{[]string{"foo", "bar"}}
	this.Print()
}
Output:

["foo","bar"]
Example (String)
package main

import (
	json "github.com/rwxrob/json"
)

func main() {
	this := json.This{"foo"}
	this.Print()
	this.This = "!some"
	this.Print()
}
Output:

"foo"
"!some"
Example (Struct)
package main

import (
	json "github.com/rwxrob/json"
)

func main() {
	this := json.This{struct {
		Foo   string
		Slice []string
	}{"foo", []string{"one", "two"}}}
	this.Print()
}
Output:

{"Foo":"foo","Slice":["one","two"]}

func (This) JSON added in v0.5.0

func (s This) JSON() ([]byte, error)

JSON implements AsJSON.

func (This) Log added in v0.5.0

func (s This) Log()

Log implements AsJSON.

func (This) Print added in v0.5.0

func (s This) Print()

Print implements AsJSON printing with fmt.Println (adding a line return).

func (This) Query added in v0.8.0

func (s This) Query(q string) (string, error)

Query provides YAML/JSON query responses.

func (This) QueryPrint added in v0.8.0

func (s This) QueryPrint(q string) error

QueryPrint prints YAML/JSON query responses.

func (This) String added in v0.5.0

func (s This) String() string

String implements AsJSON and logs any error.

func (*This) UnmarshalJSON added in v0.5.0

func (s *This) UnmarshalJSON(buf []byte) error

UnmarshalJSON implements AsJSON

Jump to

Keyboard shortcuts

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