Documentation
¶
Overview ¶
Package jsonbind provides generated, reflection-free JSON document codecs.
Index ¶
- Constants
- Variables
- func AppendAny(dst []byte, v any) []byte
- func AppendBase64(dst []byte, v []byte) []byte
- func AppendBool(dst []byte, v bool) []byte
- func AppendFloat(dst []byte, v float64) []byte
- func AppendFuncFor[T any]() (func([]byte, T) []byte, bool)
- func AppendInt(dst []byte, v int64) []byte
- func AppendRaw(dst []byte, raw []byte) []byte
- func AppendString(dst []byte, s string) []byte
- func AppendUint(dst []byte, v uint64) []byte
- func ArrayTooLong(field string, n int) error
- func DecodeJSON[T any](r io.Reader) (T, error)
- func DecodeJSONAny(raw []byte) (any, error)
- func DecodeJSONBool(raw []byte) (bool, error)
- func DecodeJSONBoolSlice(raw []byte) ([]bool, error)
- func DecodeJSONBytes[T any](data []byte) (T, error)
- func DecodeJSONFloat64(raw []byte) (float64, error)
- func DecodeJSONFloat64Slice(raw []byte) ([]float64, error)
- func DecodeJSONInt(raw []byte) (int, error)
- func DecodeJSONInt64(raw []byte) (int64, error)
- func DecodeJSONInt64Slice(raw []byte) ([]int64, error)
- func DecodeJSONIntSlice(raw []byte) ([]int, error)
- func DecodeJSONLimit[T any](r io.Reader, limit int64) (T, error)
- func DecodeJSONMapStringBool(raw []byte) (map[string]bool, error)
- func DecodeJSONMapStringFloat64(raw []byte) (map[string]float64, error)
- func DecodeJSONMapStringInt(raw []byte) (map[string]int, error)
- func DecodeJSONMapStringInt64(raw []byte) (map[string]int64, error)
- func DecodeJSONMapStringString(raw []byte) (map[string]string, error)
- func DecodeJSONString(raw []byte) (string, error)
- func DecodeJSONStringSlice(raw []byte) ([]string, error)
- func EncodeJSON[T any](w io.Writer, v T) error
- func FieldError(field, message string, cause error) error
- func GetBuffer() *[]byte
- func IsBlank(data []byte) bool
- func MaxJSONBodyBytes() int64
- func MaxNestingDepth() int
- func ParseBase64(p *Parser, field string) ([]byte, error)
- func ParseBase64Into(p *Parser, field string, dst []byte) error
- func PutBuffer(b *[]byte)
- func RawJSONArray(raw []byte) ([][]byte, error)
- func ReadLimitHint(r io.Reader, limit, hint int64) ([]byte, error)
- func RegisterAppend[T any](fn func([]byte, T) []byte)
- func RegisterDecode[T any](fn func([]byte) (T, error))
- func RegisterEncode[T any](fn func(io.Writer, T) error)
- func SetMaxJSONBodyBytes(n int64)
- func SetMaxNestingDepth(n int)
- func SortedKeys[V any](m map[string]V) []string
- type Appender
- type Declaration
- type Decoder
- type Error
- type Parser
- func (p *Parser) Any() (any, error)
- func (p *Parser) ArrayNext(n int) (bool, error)
- func (p *Parser) ArrayNextFast(n int) (more, ok bool)
- func (p *Parser) ArrayStart() (isNull bool, err error)
- func (p *Parser) ArrayStartFast() bool
- func (p *Parser) Bool() (bool, error)
- func (p *Parser) Colon() error
- func (p *Parser) ColonFast() bool
- func (p *Parser) End() error
- func (p *Parser) Float64() (float64, error)
- func (p *Parser) Int() (int, error)
- func (p *Parser) Int64() (int64, error)
- func (p *Parser) Intern(b []byte) string
- func (p *Parser) IsNull() bool
- func (p *Parser) KeyFast(n int) bool
- func (p *Parser) KeyMatched(k int) error
- func (p *Parser) ObjectKey(n int) (key []byte, ok bool, err error)
- func (p *Parser) ObjectKeyName() ([]byte, error)
- func (p *Parser) ObjectKeyRest(n int) (rest []byte, ok bool, err error)
- func (p *Parser) ObjectStart() (isNull bool, err error)
- func (p *Parser) ObjectStartFast() bool
- func (p *Parser) ParseArray[T any](field, message string, dst []T, read func(*Parser) (T, error)) error
- func (p *Parser) ParseMap[T any](field, message string, read func(*Parser) (T, error)) (map[string]T, error)
- func (p *Parser) ParseSlice[T any](field, message string, read func(*Parser) (T, error)) ([]T, error)
- func (p *Parser) RawValue() ([]byte, error)
- func (p *Parser) Release()
- func (p *Parser) Reset(data []byte)
- func (p *Parser) Rest() []byte
- func (p *Parser) Skip(k int)
- func (p *Parser) SkipValue() error
- func (p *Parser) String() (string, error)
- func (p *Parser) Uint64() (uint64, error)
- type SizeHint
Constants ¶
const DefaultMaxJSONBodyBytes int64 = 1 << 20
DefaultMaxJSONBodyBytes is the default JSON document limit (1 MiB).
const DefaultMaxNestingDepth = 90
DefaultMaxNestingDepth bounds how deeply objects and arrays may nest unless SetMaxNestingDepth raises it.
The walk is recursive — SkipValue and Any call themselves, and a generated decoder calls the next one down — so without a bound the depth of the document is the depth of the Go stack, and a request body decides it. The bound has to hold on the smallest stack this package runs on, and that is not the host's: TinyGo's goroutine stacks are fixed, and its wasm targets start with 64 KiB, on which SkipValue overflows at about a hundred open brackets. Worse, a wasm overflow is detected only at exit, so the request that caused it gets a wrong answer rather than an error. Ninety is under that with room for the frames around the parser, and nothing legitimate nests anywhere near it; encoding/json's ten thousand was the previous value and is where a host with a growable stack may put it back.
A document deeper than the bound is refused as a parse failure, which generated binders already map to 400.
Variables ¶
var ErrArrayTooLong = errors.New("jsonbind: too many array elements")
ErrArrayTooLong reports a JSON array carrying more elements than the fixed-length Go array it decodes into can hold.
It is the cause of the Error ParseArray returns, so a caller that wants to tell a too-long array from a malformed one asks errors.Is rather than matching the message.
var ErrBodyTooLarge = errors.New("jsonbind: JSON body too large")
ErrBodyTooLarge reports that a JSON document exceeded its configured limit.
var ErrIntegerRange = newError("json_parse", "JSON number out of range", nil)
ErrIntegerRange is returned by generated code when a JSON integer is outside the range of the field's declared width. The generated codec makes the comparison, because the bound is a constant it knows at generation; this is the error it has to name, and exporting one value keeps the check from needing a fmt or errors import inside a generated file.
Functions ¶
func AppendAny ¶ added in v0.4.0
AppendAny appends an arbitrary Go value produced by rest-field decoding. It covers the shapes Parser.Any yields plus the common scalar types, and any type carrying its own encoder through Appender; anything else is written as null rather than failing an otherwise valid response.
The Appender arm is what keeps a user type out of that null. Before it a value the switch did not name — which is every named struct — reached the default and encoded as null, producing a wrong document rather than a reported error. It sits after the concrete cases so a builtin shape is still matched by identity rather than by method set.
func AppendBase64 ¶ added in v0.5.24
AppendBase64 appends v as a base64 JSON string.
A nil slice and an empty one both write "", which is the rule this codec already applies to a nil slice and a nil map: nothing on the Go side separates "no bytes" from "an empty blob", so nothing on the wire does either. encoding/json writes null for the nil case.
func AppendBool ¶ added in v0.4.0
AppendBool appends a JSON boolean.
func AppendFloat ¶ added in v0.4.0
AppendFloat appends a JSON number using encoding/json's formatting: shortest round-trip, switching to exponent form outside [1e-6, 1e21) and trimming the exponent's leading zero.
func AppendFuncFor ¶ added in v0.5.21
AppendFuncFor returns T's registered append-form encoder. Callers that encode the same T repeatedly — a stream writing events — resolve it once instead of paying a registry lookup per value.
func AppendRaw ¶ added in v0.4.0
AppendRaw appends an already-encoded JSON value, or null when it is empty.
func AppendString ¶ added in v0.4.0
AppendString appends a JSON string literal. Like encoding/json's encoder it escapes <, > and & so the result is safe to embed in HTML, and U+2028/U+2029 so it stays valid JavaScript.
func AppendUint ¶ added in v0.4.0
AppendUint appends a JSON number.
func ArrayTooLong ¶ added in v0.5.34
ArrayTooLong is the error a generated decoder reports for a JSON array carrying more elements than the fixed-length Go array it decodes into can hold; it is the one [ParseArray] reports, so the two spellings agree.
func DecodeJSON ¶
DecodeJSON decodes one JSON value from r into T using a generated codec. It does not inspect HTTP headers or use reflection on T's fields.
func DecodeJSONAny ¶ added in v0.4.0
DecodeJSONAny decodes any JSON value into the Go shapes encoding/json uses for an `any` destination.
func DecodeJSONBool ¶
DecodeJSONBool decodes a JSON boolean.
func DecodeJSONBoolSlice ¶
DecodeJSONBoolSlice decodes a JSON array of booleans.
func DecodeJSONBytes ¶ added in v0.5.4
DecodeJSONBytes decodes one JSON value already held in memory into T.
The reader entries above exist because an HTTP body arrives as a stream. A caller that already has the whole document — a WebSocket message, say — has nothing to read, and going through a reader would allocate one and copy the document into a second buffer for every call.
The limit belongs to whoever produced the bytes, so none is applied here. A type carrying Decoder is read through it, on the terms EncodeJSON states for the other direction.
func DecodeJSONFloat64 ¶
DecodeJSONFloat64 decodes a JSON number as float64.
func DecodeJSONFloat64Slice ¶
DecodeJSONFloat64Slice decodes a JSON array of floats.
func DecodeJSONInt ¶
DecodeJSONInt decodes a JSON number as int.
func DecodeJSONInt64 ¶
DecodeJSONInt64 decodes a JSON number as int64.
func DecodeJSONInt64Slice ¶
DecodeJSONInt64Slice decodes a JSON array of int64s.
func DecodeJSONIntSlice ¶
DecodeJSONIntSlice decodes a JSON array of ints.
func DecodeJSONLimit ¶
DecodeJSONLimit is DecodeJSON with a per-call byte limit. A non-positive limit uses MaxJSONBodyBytes.
func DecodeJSONMapStringBool ¶ added in v0.4.0
DecodeJSONMapStringBool decodes a JSON object of booleans.
func DecodeJSONMapStringFloat64 ¶ added in v0.4.0
DecodeJSONMapStringFloat64 decodes a JSON object of floats.
func DecodeJSONMapStringInt ¶ added in v0.4.0
DecodeJSONMapStringInt decodes a JSON object of ints.
func DecodeJSONMapStringInt64 ¶ added in v0.4.0
DecodeJSONMapStringInt64 decodes a JSON object of int64s.
func DecodeJSONMapStringString ¶
DecodeJSONMapStringString decodes a JSON object of strings.
func DecodeJSONString ¶
DecodeJSONString decodes a JSON string value.
func DecodeJSONStringSlice ¶
DecodeJSONStringSlice decodes a JSON array of strings.
func EncodeJSON ¶
EncodeJSON encodes v as compact JSON to w using a generated codec, or, for a type carrying its own, through Appender.
The registry is tried first and the interface serves a type it does not hold. A type this run planned and also carries a method is, for every method the generator writes, the same code twice, and the registered function is the cheaper route: reaching a method means boxing the value or its address, which is an allocation per call. A hand-written method on a planned type therefore does not override the generated codec; a type that wants its own bytes keeps the generator away from it, which is what json:"-" and an unplanned type are for.
It does not set HTTP headers or status.
func FieldError ¶
FieldError annotates a JSON decoding error with its document field.
func GetBuffer ¶ added in v0.4.0
func GetBuffer() *[]byte
GetBuffer borrows an encode buffer. Generated code returns it with PutBuffer once the bytes have been written out.
func IsBlank ¶ added in v0.4.0
IsBlank reports whether data holds no JSON document at all. Generated decoders treat that as the zero value rather than a parse error, which is how an absent body and an empty config file have always behaved.
func MaxJSONBodyBytes ¶
func MaxJSONBodyBytes() int64
MaxJSONBodyBytes returns the effective JSON document limit.
func MaxNestingDepth ¶ added in v0.5.27
func MaxNestingDepth() int
MaxNestingDepth returns the effective nesting bound.
func ParseBase64 ¶ added in v0.5.24
ParseBase64 decodes a base64 JSON string member into a new slice. A null member decodes as a nil slice, which leaves an already-bound field alone the way a null array does.
func ParseBase64Into ¶ added in v0.5.24
ParseBase64Into decodes a base64 JSON string member into a fixed-length destination, under the same two-ended contract [ParseArray] has: a short payload fills what arrived and zeroes the rest, and one too long to fit is ErrArrayTooLong rather than a blob quietly cut to length.
func PutBuffer ¶ added in v0.4.0
func PutBuffer(b *[]byte)
PutBuffer returns an encode buffer to the pool. Oversized buffers are dropped so one large document does not pin memory for the process lifetime.
func RawJSONArray ¶
RawJSONArray splits a JSON array into its raw elements. Elements alias raw.
func ReadLimitHint ¶ added in v0.4.0
ReadLimitHint reads at most limit bytes from r, with an expected size. A caller that knows the length up front — an HTTP handler with a Content-Length, say — lets the whole body land in one allocation instead of the repeated grow-and-copy io.ReadAll performs. A wrong hint costs nothing but the usual growth.
func RegisterAppend ¶ added in v0.5.21
RegisterAppend registers a generated append-form encoder for T: the function a generated writer body is built from, appending one compact JSON value with no trailing newline.
The writer form above frames and writes a whole document, which is right for a response body but wrong for a caller composing a larger frame — an SSE event, a JSON array element — who would pay a second buffer and copy to unwrap it. This form hands the bytes over where they are wanted instead.
func RegisterDecode ¶
RegisterDecode registers a generated JSON document decoder for T.
func RegisterEncode ¶
RegisterEncode registers a generated compact JSON encoder for T.
func SetMaxJSONBodyBytes ¶
func SetMaxJSONBodyBytes(n int64)
SetMaxJSONBodyBytes changes the process-wide JSON document limit.
func SetMaxNestingDepth ¶ added in v0.5.28
func SetMaxNestingDepth(n int)
SetMaxNestingDepth changes the process-wide nesting bound. Zero or a negative value restores DefaultMaxNestingDepth. A host running on a growable stack can raise it; a TinyGo target should raise it only together with -stack-size.
func SortedKeys ¶ added in v0.4.0
SortedKeys orders map keys so a map encodes deterministically, matching encoding/json's behaviour. Generated encoders call it for every map-typed field and for payload:"*" rest maps.
Types ¶
type Appender ¶ added in v0.5.10
Appender is a type that encodes itself as JSON by appending to dst and returning the extended slice.
This is the method form of what the generator already emits, so a generated codec satisfies it by delegation and a hand-written one costs the same as the generated body would have.
There is no error result. The append path has none anywhere below this point, and every value that reaches it is one the caller already holds, so an implementation that cannot produce a document for its own value has no state worth reporting. An implementation must append valid JSON for every value of its type.
type Declaration ¶ added in v0.5.10
type Declaration struct{}
Declaration is what an annotation below returns. It carries nothing: the value exists only so the annotation can be written as a package-level declaration, which is where generation reads it.
func GenerateCodec ¶ added in v0.5.10
func GenerateCodec[T any]() Declaration
GenerateCodec asks for T's encoder and decoder, and for both methods.
func GenerateDecoder ¶ added in v0.5.10
func GenerateDecoder[T any]() Declaration
GenerateDecoder asks for T's decoder and for Decoder alone. Use it for a type only ever read.
func GenerateEncoder ¶ added in v0.5.10
func GenerateEncoder[T any]() Declaration
GenerateEncoder asks for T's encoder and for Appender alone. Use it for a type only ever written, so a decoder is not carried into the binary with it.
type Decoder ¶ added in v0.5.10
Decoder is a type that decodes one complete JSON document into itself.
data holds exactly one JSON value. The implementation fills the receiver, so the method belongs on the pointer and *T rather than T is what satisfies this.
Unlike Appender, this does not compose to any depth for free: a nested field is decoded by walking a Parser, and a method taking a complete slice joins that walk only by being handed the sub-document, which costs scanning that region twice. Generated decoders take that path for a field whose type satisfies this and no other field, so a document holding none pays nothing.
type Error ¶
type Error struct {
Code string
Message string
Field string
// contains filtered or unexported fields
}
Error describes a transport-neutral JSON mapping failure.
type Parser ¶ added in v0.4.0
type Parser struct {
// contains filtered or unexported fields
}
Parser reads a JSON document in a single forward pass. Generated codecs drive it directly: values are parsed out of the input buffer in place, so decoding allocates only the strings, slices and maps that end up in the result.
It does not use reflect and does not import encoding/json.
func NewParser ¶ added in v0.4.0
NewParser returns a Parser reading data. data is not copied, and values returned by RawValue alias it.
func (*Parser) Any ¶ added in v0.4.0
Any decodes an arbitrary JSON value into the same Go shapes encoding/json produces for an `any` destination.
func (*Parser) ArrayNext ¶ added in v0.4.0
ArrayNext reports whether another element follows. n is the element index.
func (*Parser) ArrayNextFast ¶ added in v0.5.34
ArrayNextFast answers ArrayNext when the next byte settles it: a comma after an earlier element, the closing bracket, or a value's first byte for the first element. ok is false when ArrayNext has to look.
func (*Parser) ArrayStart ¶ added in v0.4.0
ArrayStart consumes '['. A JSON null reports isNull and consumes the literal.
func (*Parser) ArrayStartFast ¶ added in v0.5.34
ArrayStartFast consumes '[' when it is right here and the nesting bound allows it. A false means ArrayStart has to look.
func (*Parser) Colon ¶ added in v0.5.34
Colon consumes the colon after a member name, whitespace before it included.
func (*Parser) ColonFast ¶ added in v0.5.34
ColonFast consumes the colon after a member name when it is right here. A false means Colon has to look.
func (*Parser) End ¶ added in v0.4.0
End reports an error when anything but whitespace follows the document.
func (*Parser) Float64 ¶ added in v0.4.0
Float64 decodes a JSON number. null decodes as 0.
A number with no exponent whose digits fit an exactly representable integer is read in one pass, as parseFloatFast reads a span; the grammar the pass accepts is the JSON one, and everything it does not accept goes through numberSpan and strconv.
func (*Parser) Int64 ¶ added in v0.4.0
Int64 decodes a JSON number as int64. null decodes as 0.
A plain integer of up to eighteen digits is read in one pass, straight off the input; anything else -- a fraction, an exponent, a leading zero followed by more, a nineteenth digit, whitespace, null -- goes through the grammar check and the checked conversion the slow half keeps.
func (*Parser) Intern ¶ added in v0.5.34
Intern returns b as a string, sharing an earlier allocation when the cache holds an equal one. Generated decoders use it for map keys, which recur across the objects of one document more than any value does. b is taken as it is, with no validation.
func (*Parser) KeyFast ¶ added in v0.5.34
KeyFast positions the parser at member n's opening quote when the comma and the quote are right here, and reports whether it did; Rest then hands the unread input to the name tree. A false means ObjectKeyRest has to look, which is also how the closing brace is reached.
func (*Parser) KeyMatched ¶ added in v0.5.34
KeyMatched consumes the k bytes of member name, quotes included, that the caller matched at the start of ObjectKeyRest's result, then the colon.
func (*Parser) ObjectKey ¶ added in v0.4.0
ObjectKey returns the next member name, or ok=false at '}'. n is the zero-based member index and tells the parser whether a comma is required, so the parser needs no nesting stack of its own.
The returned key aliases parser scratch space and is only valid until the next ObjectKey call.
func (*Parser) ObjectKeyName ¶ added in v0.5.34
ObjectKeyName reads the member name ObjectKeyRest positioned at, unescaped, and the colon after it. The name aliases parser scratch space and is valid until the next member is read.
func (*Parser) ObjectKeyRest ¶ added in v0.5.34
ObjectKeyRest positions the parser at the next member name and returns the unread input from that name's opening quote, or ok=false at '}'. n is the zero-based member index, as for ObjectKey.
It is the first half of ObjectKey, split out so a generated decoder can match its member names against the input in place: a name that matches a literal is consumed with KeyMatched, and one that does not is read the ordinary way with ObjectKeyName. The returned slice aliases the input and is not advanced past.
func (*Parser) ObjectStart ¶ added in v0.4.0
ObjectStart consumes '{'. A JSON null reports isNull and consumes the literal.
func (*Parser) ObjectStartFast ¶ added in v0.5.34
ObjectStartFast consumes '{' when it is right here and the nesting bound allows it. A false means ObjectStart has to look.
func (*Parser) ParseArray ¶ added in v0.5.28
func (p *Parser) ParseArray[T any](field, message string, dst []T, read func(*Parser) (T, error)) error
ParseArray decodes a JSON array field into a fixed-length destination, which the caller passes as a slice over its array: p.ParseArray("cells", msg, out.Cells[:], read).
The two ends of a fixed length are not symmetric. A short array fills what arrived and leaves the rest at the zero value, because a length the Go type states is not a length the document has to restate. A long one is an error: storing the first len(dst) elements would drop the tail, and a decoder that silently loses data is the failure a declared length exists to prevent.
The tail is zeroed rather than left alone, so a member that arrives twice decodes to the second array rather than to the two overlaid.
A JSON null leaves the destination untouched, as [ParseSlice] does. Errors are annotated the same way as ParseSlice; a too-long array reports ErrArrayTooLong as its cause.
func (*Parser) ParseMap ¶ added in v0.5.28
func (p *Parser) ParseMap[T any](field, message string, read func(*Parser) (T, error)) (map[string]T, error)
ParseMap decodes a JSON object field, reading each member value with read. A JSON null decodes as a nil map and an empty object as a non-nil empty one. Errors are annotated the same way as ParseSlice.
func (*Parser) ParseSlice ¶ added in v0.5.28
func (p *Parser) ParseSlice[T any](field, message string, read func(*Parser) (T, error)) ([]T, error)
ParseSlice decodes a JSON array field, reading each element with read. A JSON null decodes as a nil slice and an empty array as a non-nil empty one. Structural errors are annotated with the field's document name; an element error is annotated with message, or passed through unchanged when message is empty so a nested decoder can report its own fields.
The element type is the method's own type parameter, which is what kept this and its siblings package functions before Go 1.27; it is inferred from read.
func (*Parser) RawValue ¶ added in v0.4.0
RawValue returns the next value's bytes as a subslice of the input. The result aliases the parser's buffer and must be copied to outlive it.
func (*Parser) Release ¶ added in v0.5.34
func (p *Parser) Release()
Release hands the parser's string cache back to the pool, so the next decode on any parser starts from what this one learned. A parser that is never released keeps its table until it is collected, which costs the pool a table and nothing else.
func (*Parser) SkipValue ¶ added in v0.4.0
SkipValue advances past the next value, whatever its shape. Structure is validated; the value's contents are not interpreted.
func (*Parser) String ¶ added in v0.4.0
String decodes a JSON string. null decodes as "".
The common string -- the quote right here, no escape, and a value the cache holds or a plain allocation -- is settled in this one function: the word scan is written here rather than reached through stringSpan, and the cache hit rather than through intern, so a string costs one call and a miss one more for its allocation. Whitespace before the value, null, an escape, a control byte and every error go through stringSlow, which is the general path as it was.
func (*Parser) Uint64 ¶ added in v0.5.23
Uint64 decodes a JSON number as uint64. null decodes as 0, and a negative number is an error rather than a wrapped value.
This is the one unsigned reader. The narrower unsigned widths are read through it and range-checked by the generated codec against bounds it knows at generation, which keeps eight more methods out of the runtime a TinyGo target links.
type SizeHint ¶ added in v0.5.34
type SizeHint struct {
// contains filtered or unexported fields
}
SizeHint remembers how large a type's encoding has been, so an encoder that must hand its result to the caller allocates once at that size rather than growing through several reallocations. Generated AppendJSONTo methods keep one per type. The zero value is ready to use and safe for concurrent use.