jettison

package module
Version: v0.7.4 Latest Latest
Warning

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

Go to latest
Published: Mar 21, 2022 License: MIT Imports: 19 Imported by: 15

README

Jettison

GoCaptain

Jettison is a fast and flexible JSON encoder for the Go programming language, inspired by bet365/jingo, with a richer features set, aiming at 100% compatibility with the standard library.



Installation

Jettison uses Go modules. Releases are tagged according to the SemVer format, prefixed with a v, starting from 0.2.0. You can get the latest release using the following command.

$ go get github.com/wI2L/jettison@latest

⚠️ From version v0.7.4, the packages requires Go 1.17+ to build, due to the usage of the new build constraints.

Key features

  • Fast, see benchmarks
  • No dynamic memory allocations in hot paths
  • Behavior identical to the standard library by default
  • No code generation required
  • Clear and concise API
  • Configurable with opt-in functional options
  • Native support for many standard library types, see improvements
  • Custom AppendMarshaler interface to avoid allocations
  • Extensive testsuite that compares its output against encoding/json

Overview

The goal of Jettision is to take up the idea introduced by the bet365/jingo package and build a fully-featured JSON encoder around it, that comply with the behavior of the encoding/json package. Unlike the latter, Jettison does not use reflection during marshaling, but only once to create the instruction set for a given type ahead of time. The drawback to this approach requires to instantiate an instruction-set once for each type that needs to be marshaled, but that is overcomed with a package cache.

The package aims to have a behavior similar to that of the standard library for all types encoding and struct tags, meaning that the documentation of the json.Marshal function is applicable for Jettison, with a few exceptions described in this section. As such, most of the tests compare their output against it to guarantee that.

Implementation details

The main concept of Jettison consists of using pre-build instructions-set to reduce the cost of using the reflect package at runtime. When marshaling a value, a set of instructions is recursively generated for its type, which defines how to iteratively encode it. An instruction is a function or a closure, that have all the information required to read the data from memory using unsafe operations (pointer type conversion, arithmetic...) during the instruction set execution.

Differences with encoding/json

All notable differences with the standard library behavior are listed below. Please note that these might evolve with future versions of the package.

Improvements
  • The time.Time and time.Duration types are handled natively. For time values, the encoder doesn't invoke MarshalJSON or MarshalText, but use the time.AppendFormat function instead, and write the result to the stream. Similarly, for durations, it isn't necessary to implements the json.Marshaler or encoding.TextMarshaler interfaces on a custom wrapper type, the encoder uses the result of one of the methods Minutes, Seconds, Nanoseconds or String, based on the duration format configured.

  • The sync.Map type is handled natively. The marshaling behavior is similar to the one of a standard Go map. The option UnsortedMap can also be used in cunjunction with this type to disable the default keys sort.

  • The omitnil field tag's option can be used to specify that a field with a nil pointer should be omitted from the encoding. This option has precedence over the omitempty option.

Bugs
Go1.13 and backward
  • Nil map keys values implementing the encoding.TextMarshaler interface are encoded as empty strings, while the encoding/json package currently panic because of that. See this issue for more details.[1]

  • Nil struct fields implementing the encoding.TextMarshaler interface are encoded as null, while the encoding/json package currently panic because of that. See this issue for more details.[1]

1: The issues mentioned above have had their associated CL merged, and was released with Go 1.14.

Usage

Basic

As stated above, the library behave similarly to the encoding/json package. You can simply replace the json.Marshal function with jettison.Marshal, and expect the same output with better performances.

type X struct {
   A string `json:"a"`
   B int64  `json:"b"`
}
b, err := jettison.Marshal(X{
   A: "Loreum",
   B: 42,
})
if err != nil {
   log.Fatal(err)
}
os.Stdout.Write(b)
Result
{"a":"Loreum","b":42}

Advanced

If more control over the encoding behavior is required, use the MarshalOpts function instead. The second parameter is variadic and accept a list of functional opt-in options described below:

name description
TimeLayout Defines the layout used to encode time.Time values. The layout must be compatible with the AppendFormat method.
DurationFormat Defines the format used to encode time.Duration values. See the documentation of the DurationFmt type for the complete list of formats available.
UnixTime Encode time.Time values as JSON numbers representing Unix timestamps, the number of seconds elapsed since January 1, 1970 UTC. This option has precedence over TimeLayout.
UnsortedMap Disables map keys sort.
ByteArrayAsString Encodes byte arrays as JSON strings rather than JSON arrays. The output is subject to the same escaping rules used for JSON strings, unless the option NoStringEscaping is used.
RawByteSlice Disables the base64 default encoding used for byte slices.
NilMapEmpty Encodes nil Go maps as empty JSON objects rather than null.
NilSliceEmpty Encodes nil Go slices as empty JSON arrays rather than null.
NoStringEscaping Disables string escaping. NoHTMLEscaping and NoUTF8Coercion are ignored when this option is used.
NoHTMLEscaping Disables the escaping of special HTML characters such as &, < and > in JSON strings. This is similar to json.Encoder.SetEscapeHTML(false).
NoUTF8Coercion Disables the replacement of invalid bytes with the Unicode replacement rune in JSON strings.
AllowList Sets a whitelist that represents which fields are to be encoded when marshaling a Go struct.
DenyList Sets a blacklist that represents which fields are ignored during the marshaling of a Go struct.
NoCompact Disables the compaction of JSON output produced by MarshalJSON method, and json.RawMessage values.
NoNumberValidation Disables the validation of json.Number values.
WithContext Sets the context.Context to be passed to invocations of AppendJSONContext methods.

Take a look at the examples to see these options in action.

Benchmarks

If you'd like to run the benchmarks yourself, use the following command.

go get github.com/cespare/prettybench
go test -bench=. | prettybench

Results -short

These benchmarks were run 10x (statistics computed with benchstat) on a MacBook Pro 15", with the following specs:

OS:  macOS Catalina (10.15.7)
CPU: 2.6 GHz Intel Core i7
Mem: 16GB
Go:  go version go1.17 darwin/amd64
Tag: v0.7.2
Stats
name                    time/op
Simple/standard-8          573ns ± 1%
Simple/jsoniter-8          547ns ± 0%
Simple/segmentj-8          262ns ± 1%
Simple/jettison-8          408ns ± 1%
Complex/standard-8        11.7µs ± 0%
Complex/jsoniter-8        11.6µs ± 1%
Complex/segmentj-8        7.96µs ± 0%
Complex/jettison-8        5.90µs ± 1%
CodeMarshal/standard-8    6.71ms ± 0%
CodeMarshal/jsoniter-8    6.35ms ± 1%
CodeMarshal/segmentj-8    4.38ms ± 1%
CodeMarshal/jettison-8    5.56ms ± 1%
Map/standard-8            1.83µs ± 1%
Map/jsoniter-8            1.65µs ± 0%
Map/segmentj-8            1.61µs ± 0%
Map/jettison-8             772ns ± 1%
Map/jettison-nosort-8      507ns ± 1%

name speed Simple/standard-8 236MB/s ± 1% Simple/jsoniter-8 247MB/s ± 0% Simple/segmentj-8 516MB/s ± 1% Simple/jettison-8 331MB/s ± 1% Complex/standard-8 72.9MB/s ± 0% Complex/jsoniter-8 70.6MB/s ± 0% Complex/segmentj-8 108MB/s ± 0% Complex/jettison-8 144MB/s ± 1% CodeMarshal/standard-8 289MB/s ± 0% CodeMarshal/jsoniter-8 306MB/s ± 1% CodeMarshal/segmentj-8 443MB/s ± 1% CodeMarshal/jettison-8 349MB/s ± 1% Map/standard-8 46.6MB/s ± 1% Map/jsoniter-8 51.5MB/s ± 0% Map/segmentj-8 52.8MB/s ± 0% Map/jettison-8 110MB/s ± 1% Map/jettison-nosort-8 168MB/s ± 1%

name alloc/op Simple/standard-8 144B ± 0% Simple/jsoniter-8 152B ± 0% Simple/segmentj-8 144B ± 0% Simple/jettison-8 144B ± 0% Complex/standard-8 4.05kB ± 0% Complex/jsoniter-8 3.95kB ± 0% Complex/segmentj-8 2.56kB ± 0% Complex/jettison-8 935B ± 0% CodeMarshal/standard-8 1.97MB ± 0% CodeMarshal/jsoniter-8 2.00MB ± 0% CodeMarshal/segmentj-8 1.98MB ± 2% CodeMarshal/jettison-8 1.98MB ± 2% Map/standard-8 888B ± 0% Map/jsoniter-8 884B ± 0% Map/segmentj-8 576B ± 0% Map/jettison-8 96.0B ± 0% Map/jettison-nosort-8 160B ± 0%

name allocs/op Simple/standard-8 1.00 ± 0% Simple/jsoniter-8 2.00 ± 0% Simple/segmentj-8 1.00 ± 0% Simple/jettison-8 1.00 ± 0% Complex/standard-8 79.0 ± 0% Complex/jsoniter-8 71.0 ± 0% Complex/segmentj-8 52.0 ± 0% Complex/jettison-8 8.00 ± 0% CodeMarshal/standard-8 1.00 ± 0% CodeMarshal/jsoniter-8 2.00 ± 0% CodeMarshal/segmentj-8 1.00 ± 0% CodeMarshal/jettison-8 1.00 ± 0% Map/standard-8 19.0 ± 0% Map/jsoniter-8 14.0 ± 0% Map/segmentj-8 18.0 ± 0% Map/jettison-8 1.00 ± 0% Map/jettison-nosort-8 2.00 ± 0%

Simple [source]

Basic payload with fields of type string, int and bool.

Simple Benchmark Graph

Complex [source]

Large payload with a variety of composite Go types, such as struct, map, interface, multi-dimensions array and slice, with pointer and non-pointer value types.

Please note that this test is somewhat positively influenced by the performances of map marshaling.

Complex Benchmark Graph

CodeMarshal [source]

Borrowed from the encoding/json tests. See testdata/code.json.gz.

CodeMarshal Benchmark Graph

Map [source]

Simple map[string]int with 6 keys.

Map Graph

Credits

This library and its design has been inspired by the work of others at @bet365 and @segmentio. See the following projects for reference:

License

Jettison is licensed under the MIT license. See the LICENSE file.

This package also uses some portions of code from the Go encoding/json package. The associated license can be found in LICENSE.golang.

Documentation

Overview

Example (CustomMarshaler)
package main

import (
	"log"
	"os"
	"strconv"

	"github.com/wI2L/jettison"
)

type Animal int

const (
	Unknown Animal = iota
	Gopher
	Zebra
)

// AppendJSON implements the jettison.AppendMarshaler interface.
func (a Animal) AppendJSON(dst []byte) ([]byte, error) {
	var s string
	switch a {
	default:
		s = "unknown"
	case Gopher:
		s = "gopher"
	case Zebra:
		s = "zebra"
	}
	dst = append(dst, strconv.Quote(s)...)
	return dst, nil
}

func main() {
	zoo := []Animal{
		Unknown,
		Zebra,
		Gopher,
	}
	b, err := jettison.Marshal(zoo)
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(b)
}
Output:

["unknown","zebra","gopher"]

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Append added in v0.5.0

func Append(dst []byte, v interface{}) ([]byte, error)

Append is similar to Marshal but appends the JSON representation of v to dst instead of returning a new allocated slice.

Example
package main

import (
	"log"
	"os"

	"github.com/wI2L/jettison"
)

func main() {
	type X struct {
		A bool              `json:"a"`
		B uint32            `json:"b"`
		C map[string]string `json:"users"`
	}
	x := X{
		A: true,
		B: 42,
		C: map[string]string{
			"bob":   "admin",
			"jerry": "user",
		},
	}
	buf, err := jettison.Append([]byte(nil), x)
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(buf)
}
Output:

{"a":true,"b":42,"users":{"bob":"admin","jerry":"user"}}

func AppendOpts added in v0.5.0

func AppendOpts(dst []byte, v interface{}, opts ...Option) ([]byte, error)

AppendOpts is similar to Append, but also accepts a list of options to configure the encoding behavior.

Example
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/wI2L/jettison"
)

func main() {
	for _, v := range []interface{}{
		nil, 2 * time.Second,
	} {
		buf, err := jettison.AppendOpts([]byte(nil), v,
			jettison.DurationFormat(jettison.DurationString),
		)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(buf))
	}
}
Output:

null
"2s"

func Marshal added in v0.3.0

func Marshal(v interface{}) ([]byte, error)

Marshal returns the JSON encoding of v. The full documentation can be found at https://golang.org/pkg/encoding/json/#Marshal.

Example
package main

import (
	"log"
	"os"

	"github.com/wI2L/jettison"
)

func main() {
	type X struct {
		A string   `json:"a"`
		B int64    `json:"b"`
		C []string `json:"colors"`
	}
	x := X{
		A: "Loreum",
		B: -42,
		C: []string{"blue", "white", "red"},
	}
	b, err := jettison.Marshal(x)
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(b)
}
Output:

{"a":"Loreum","b":-42,"colors":["blue","white","red"]}

func MarshalOpts added in v0.5.0

func MarshalOpts(v interface{}, opts ...Option) ([]byte, error)

MarshalOpts is similar to Marshal, but also accepts a list of options to configure the encoding behavior.

Types

type AppendMarshaler added in v0.5.0

type AppendMarshaler interface {
	AppendJSON([]byte) ([]byte, error)
}

AppendMarshaler is a variant of the json.Marshaler interface, implemented by types that can append a valid and compact JSON representation of themselves to a buffer. If a type implements both interfaces, this one will be used in priority by the package.

type AppendMarshalerCtx added in v0.5.0

type AppendMarshalerCtx interface {
	AppendJSONContext(context.Context, []byte) ([]byte, error)
}

AppendMarshalerCtx is similar to AppendMarshaler, but the method implemented also takes a context. The use case for this interface is to dynamically control the marshaling of the type implementing it through the values encapsulated by the context, that may be provided at runtime using WithContext.

type DurationFmt

type DurationFmt int

DurationFmt represents the format used to encode a time.Duration value.

const (
	DurationString DurationFmt = iota
	DurationMinutes
	DurationSeconds
	DurationMilliseconds
	DurationMicroseconds
	DurationNanoseconds // default
)

DurationFmt constants.

func (DurationFmt) String

func (f DurationFmt) String() string

String implements the fmt.Stringer interface for DurationFmt.

type InvalidOptionError added in v0.5.0

type InvalidOptionError struct {
	Err error
}

InvalidOptionError is the error returned by MarshalOpts when one of the given options is invalid.

func (*InvalidOptionError) Error added in v0.5.0

func (e *InvalidOptionError) Error() string

Error implements the builtin error interface.

type MarshalerError

type MarshalerError struct {
	Type reflect.Type
	Err  error
	// contains filtered or unexported fields
}

MarshalerError represents an error from calling the methods MarshalJSON or MarshalText.

func (*MarshalerError) Error

func (e *MarshalerError) Error() string

Error implements the builtin error interface.

func (*MarshalerError) Unwrap added in v0.3.0

func (e *MarshalerError) Unwrap() error

Unwrap returns the error wrapped by e. This doesn't implement a public interface, but allow to use the errors.Unwrap function released in Go1.13 with a MarshalerError.

type Option

type Option func(*encOpts)

An Option overrides the default encoding behavior of the MarshalOpts function.

func AllowList added in v0.5.0

func AllowList(fields []string) Option

AllowList sets the list of first-level fields which are to be considered when encoding a struct. The fields are identified by the name that is used in the final JSON payload. See DenyFields documentation for more information regarding joint use with this option.

Example
package main

import (
	"fmt"
	"log"

	"github.com/wI2L/jettison"
)

func main() {
	type Z struct {
		Omega int `json:"ω"`
	}
	type Y struct {
		Pi string `json:"π"`
	}
	type X struct {
		Z     Z      `json:"Z"`
		Alpha string `json:"α"`
		Beta  string `json:"β"`
		Gamma string
		Y
	}
	x := X{
		Z:     Z{Omega: 42},
		Alpha: "1",
		Beta:  "2",
		Gamma: "3",
		Y:     Y{Pi: "4"},
	}
	for _, opt := range []jettison.Option{
		nil, jettison.AllowList([]string{"Z", "β", "Gamma", "π"}),
	} {
		b, err := jettison.MarshalOpts(x, opt)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(b))
	}
}
Output:

{"Z":{"ω":42},"α":"1","β":"2","Gamma":"3","π":"4"}
{"Z":{},"β":"2","Gamma":"3","π":"4"}

func ByteArrayAsString

func ByteArrayAsString() Option

ByteArrayAsString configures an encoder to encode byte arrays as raw JSON strings.

Example
package main

import (
	"fmt"
	"log"

	"github.com/wI2L/jettison"
)

func main() {
	b1 := [6]byte{'L', 'o', 'r', 'e', 'u', 'm'}
	b2 := [6]*byte{&b1[0], &b1[1], &b1[2], &b1[3], &b1[4], &b1[5]}

	for _, opt := range []jettison.Option{
		nil, jettison.ByteArrayAsString(),
	} {
		for _, v := range []interface{}{b1, b2} {
			b, err := jettison.MarshalOpts(v, opt)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("%s\n", string(b))
		}
	}
}
Output:

[76,111,114,101,117,109]
[76,111,114,101,117,109]
"Loreum"
[76,111,114,101,117,109]

func DenyList added in v0.5.0

func DenyList(fields []string) Option

DenyList is similar to AllowList, but conversely sets the list of fields to omit during encoding. When used in cunjunction with AllowList, denied fields have precedence over the allowed fields.

Example
package main

import (
	"fmt"
	"log"

	"github.com/wI2L/jettison"
)

func main() {
	type X struct {
		A int  `json:"aaAh"`
		B bool `json:"buzz"`
		C string
		D uint
	}
	x := X{
		A: -42,
		B: true,
		C: "Loreum",
		D: 42,
	}
	for _, opt := range []jettison.Option{
		nil, jettison.DenyList([]string{"buzz", "D"}),
	} {
		b, err := jettison.MarshalOpts(x, opt)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(b))
	}
}
Output:

{"aaAh":-42,"buzz":true,"C":"Loreum","D":42}
{"aaAh":-42,"C":"Loreum"}

func DurationFormat

func DurationFormat(format DurationFmt) Option

DurationFormat sets the format used to encode time.Duration values.

Example
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/wI2L/jettison"
)

func main() {
	d := 1*time.Hour + 3*time.Minute + 2*time.Second + 66*time.Millisecond

	for _, format := range []jettison.DurationFmt{
		jettison.DurationString,
		jettison.DurationMinutes,
		jettison.DurationSeconds,
		jettison.DurationMilliseconds,
		jettison.DurationMicroseconds,
		jettison.DurationNanoseconds,
	} {
		b, err := jettison.MarshalOpts(d, jettison.DurationFormat(format))
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(b))
	}
}
Output:

"1h3m2.066s"
63.03443333333333
3782.066
3782066
3782066000
3782066000000

func NilMapEmpty

func NilMapEmpty() Option

NilMapEmpty configures an encoder to encode nil Go maps as empty JSON objects, rather than null.

Example
package main

import (
	"fmt"
	"log"

	"github.com/wI2L/jettison"
)

func main() {
	type X struct {
		M1 map[string]int
		M2 map[int]string
	}
	x := X{
		M1: map[string]int{},
		M2: nil,
	}
	for _, opt := range []jettison.Option{
		nil, jettison.NilMapEmpty(),
	} {
		b, err := jettison.MarshalOpts(x, opt)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(b))
	}
}
Output:

{"M1":{},"M2":null}
{"M1":{},"M2":{}}

func NilSliceEmpty

func NilSliceEmpty() Option

NilSliceEmpty configures an encoder to encode nil Go slices as empty JSON arrays, rather than null.

Example
package main

import (
	"fmt"
	"log"

	"github.com/wI2L/jettison"
)

func main() {
	type X struct {
		S1 []int
		S2 []string
	}
	x := X{
		S1: []int{},
		S2: nil,
	}
	for _, opt := range []jettison.Option{
		nil, jettison.NilSliceEmpty(),
	} {
		b, err := jettison.MarshalOpts(x, opt)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(b))
	}
}
Output:

{"S1":[],"S2":null}
{"S1":[],"S2":[]}

func NoCompact added in v0.5.0

func NoCompact() Option

NoCompact configures an encoder to disable the compaction of the JSON output produced by a call to MarshalJSON, or the content of a json.RawMessage. see https://golang.org/pkg/encoding/json/#Compact

Example
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/wI2L/jettison"
)

func main() {
	rm := json.RawMessage(`{ "a":"b" }`)
	for _, opt := range []jettison.Option{
		nil, jettison.NoCompact(),
	} {
		b, err := jettison.MarshalOpts(rm, opt)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(b))
	}
}
Output:

{"a":"b"}
{ "a":"b" }

func NoHTMLEscaping added in v0.3.1

func NoHTMLEscaping() Option

NoHTMLEscaping configures an encoder to disable the escaping of problematic HTML characters in JSON strings.

func NoNumberValidation added in v0.5.0

func NoNumberValidation() Option

NoNumberValidation configures an encoder to disable the validation of json.Number values.

func NoStringEscaping

func NoStringEscaping() Option

NoStringEscaping configures an encoder to disable string escaping.

func NoUTF8Coercion added in v0.3.1

func NoUTF8Coercion() Option

NoUTF8Coercion configures an encoder to disable UTF8 coercion that replace invalid bytes with the Unicode replacement rune.

func RawByteSlice added in v0.4.0

func RawByteSlice() Option

RawByteSlice configures an encoder to encode byte slices as raw JSON strings, rather than bas64-encoded strings.

Example
package main

import (
	"fmt"
	"log"

	"github.com/wI2L/jettison"
)

func main() {
	bs := []byte("Loreum Ipsum")

	for _, opt := range []jettison.Option{
		nil, jettison.RawByteSlice(),
	} {
		b, err := jettison.MarshalOpts(bs, opt)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\n", string(b))
	}
}
Output:

"TG9yZXVtIElwc3Vt"
"Loreum Ipsum"

func TimeLayout

func TimeLayout(layout string) Option

TimeLayout sets the time layout used to encode time.Time values. The layout must be compatible with the Golang time package specification.

Example
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/wI2L/jettison"
)

func main() {
	t := time.Date(2042, time.July, 25, 16, 42, 24, 67850, time.UTC)

	locs := []*time.Location{
		time.UTC, time.FixedZone("WTF", 666), time.FixedZone("LOL", -4242),
	}
	for _, layout := range []string{
		time.RFC3339,
		time.RFC822,
		time.RFC1123Z,
		time.RFC3339Nano, // default
	} {
		for _, loc := range locs {
			b, err := jettison.MarshalOpts(t.In(loc), jettison.TimeLayout(layout))
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("%s\n", string(b))
		}
	}
}
Output:

"2042-07-25T16:42:24Z"
"2042-07-25T16:53:30+00:11"
"2042-07-25T15:31:42-01:10"
"25 Jul 42 16:42 UTC"
"25 Jul 42 16:53 WTF"
"25 Jul 42 15:31 LOL"
"Fri, 25 Jul 2042 16:42:24 +0000"
"Fri, 25 Jul 2042 16:53:30 +0011"
"Fri, 25 Jul 2042 15:31:42 -0110"
"2042-07-25T16:42:24.00006785Z"
"2042-07-25T16:53:30.00006785+00:11"
"2042-07-25T15:31:42.00006785-01:10"

func UnixTime added in v0.5.0

func UnixTime() Option

UnixTime configures an encoder to encode time.Time values as Unix timestamps. This option, when used, has precedence over any time layout confiured.

Example
package main

import (
	"log"
	"os"
	"time"

	"github.com/wI2L/jettison"
)

func main() {
	t := time.Date(2024, time.December, 24, 12, 24, 42, 0, time.UTC)

	b, err := jettison.MarshalOpts(t, jettison.UnixTime())
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(b)
}
Output:

1735043082

func UnsortedMap

func UnsortedMap() Option

UnsortedMap configures an encoder to skip the sort of map keys.

Example
package main

import (
	"encoding/json"
	"log"
	"os"

	"github.com/wI2L/jettison"
)

func main() {
	m := map[int]string{
		3: "three",
		1: "one",
		2: "two",
	}
	b, err := jettison.MarshalOpts(m, jettison.UnsortedMap())
	if err != nil {
		log.Fatal(err)
	}
	var sorted map[int]string
	if err := json.Unmarshal(b, &sorted); err != nil {
		log.Fatal(err)
	}
	b, err = jettison.Marshal(sorted)
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(b)
}
Output:

{"1":"one","2":"two","3":"three"}

func WithContext added in v0.5.0

func WithContext(ctx context.Context) Option

WithContext sets the context to use during encoding. The context will be passed in to the AppendJSONContext method of types that implement the AppendMarshalerCtx interface.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"strconv"

	"github.com/wI2L/jettison"
)

type (
	secret string
	ctxKey string
)

const obfuscateKey = ctxKey("_obfuscate_")

// AppendJSONContext implements the jettison.AppendMarshalerCtx interface.
func (s secret) AppendJSONContext(ctx context.Context, dst []byte) ([]byte, error) {
	out := string(s)
	if v := ctx.Value(obfuscateKey); v != nil {
		if hide, ok := v.(bool); ok && hide {
			out = "**__SECRET__**"
		}
	}
	dst = append(dst, strconv.Quote(out)...)
	return dst, nil
}

func main() {
	sec := secret("v3ryS3nSitiv3P4ssWord")

	b, err := jettison.Marshal(sec)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", string(b))

	ctx := context.WithValue(context.Background(),
		obfuscateKey, true,
	)
	b, err = jettison.MarshalOpts(sec, jettison.WithContext(ctx))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", string(b))
}
Output:

"v3ryS3nSitiv3P4ssWord"
"**__SECRET__**"

type SyntaxError added in v0.5.0

type SyntaxError struct {
	Offset int64
	// contains filtered or unexported fields
}

A SyntaxError is a description of a JSON syntax error. Unlike its equivalent in the encoding/json package, the Error method implemented does not return a meaningful message, and the Offset field is always zero. It is present merely for consistency.

func (*SyntaxError) Error added in v0.5.0

func (e *SyntaxError) Error() string

Error implements the builtin error interface.

type UnsupportedTypeError

type UnsupportedTypeError struct {
	Type reflect.Type
}

UnsupportedTypeError is the error returned by Marshal when attempting to encode an unsupported value type.

func (*UnsupportedTypeError) Error

func (e *UnsupportedTypeError) Error() string

Error implements the bultin error interface.

type UnsupportedValueError

type UnsupportedValueError struct {
	Value reflect.Value
	Str   string
}

UnsupportedValueError is the error returned by Marshal when attempting to encode an unsupported value.

func (*UnsupportedValueError) Error

func (e *UnsupportedValueError) Error() string

Error implements the builtin error interface.

Directories

Path Synopsis
tools

Jump to

Keyboard shortcuts

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