Documentation
¶
Overview ¶
Package simdjson parses JSON by finding the whole document's structure in a few vector passes, then walking that instead of the bytes.
It is built on [simd.go](https://github.com/sebishogun/simd), so it needs no cgo and runs the same way on amd64, arm64, riscv64, s390x, ppc64le and loong64 — unlike the existing Go ports of simdjson, which are amd64 with hand-written assembly.
doc, err := simdjson.Parse(data)
name := doc.Get("user", "name").String()
age := doc.Get("user", "age").Int()
How it works ¶
Two stages, which is the design simdjson introduced.
Stage one finds every structural character — the braces, brackets, colons and commas — in one vector pass each, and works out which quotes really open and close strings rather than being escaped. A conventional parser reads a byte and branches on what it is, which is a dependent and unpredictable branch per byte; this makes eight branch-free passes over the document instead, and eight passes with no branches beat one pass with a branch per byte.
Stage two walks those positions. A document of a megabyte might have fifty thousand structural characters, so the second stage sees fifty thousand items rather than a million bytes.
What it is for ¶
Pulling a few values out of a document, which is most of what JSON is used for and the case encoding/json is worst at — it decodes everything to reach anything. Doc.Get navigates the index without decoding what it passes.
It is not a replacement for encoding/json. There is no struct unmarshalling, no tags, no interfaces, no streaming. If you want a Go value, use the standard library; if you want three fields out of a large payload, this is several times faster.
Example ¶
The case this package is for: a few values out of a document, without decoding the rest of it.
package main
import (
"fmt"
"github.com/sebishogun/simdjson"
)
func main() {
data := []byte(`{
"user": {"name": "ada", "age": 36, "tags": ["math", "engines"]},
"meta": {"page": 1}
}`)
doc, err := simdjson.Parse(data)
if err != nil {
fmt.Println("bad json:", err)
return
}
fmt.Println(doc.Get("user", "name").String())
fmt.Println(doc.Get("user", "age").Int())
fmt.Println(doc.Get("user", "tags").Index(1).String())
}
Output: ada 36 engines
Index ¶
- Variables
- func AppendBool(dst []byte, v bool) []byte
- func AppendFloat(dst []byte, v float64, bits int) []byte
- func AppendInt(dst []byte, v int64) []byte
- func AppendString(dst []byte, s string, opts Options) []byte
- func AppendUint(dst []byte, v uint64) []byte
- func Compact(dst *bytes.Buffer, src []byte) error
- func DeletePath(data []byte, path string) ([]byte, error)
- func ForEachLine(data []byte, fn func(Value) bool) error
- func ForEachLineReader(r io.Reader, fn func(Value) bool) error
- func ForEachLineReaderParallel(r io.Reader, fn func(Value) bool) error
- func HTMLEscape(dst *bytes.Buffer, src []byte)
- func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
- func Marshal(v any) ([]byte, error)
- func MarshalIndent(v any, prefix, indent string) ([]byte, error)
- func MarshalTo(dst []byte, v any) ([]byte, error)
- func MarshalWrite(w io.Writer, v any) error
- func RegisterEncoder[T any](fn AppendFunc)
- func SetPath(data []byte, path string, v any) ([]byte, error)
- func SetRawPath(data []byte, path string, raw []byte) ([]byte, error)
- func Skip(data []byte) (start, end int, ok bool)
- func Unmarshal(data []byte, v any) error
- func Valid(data []byte) bool
- type AppendFunc
- type Decoder
- func (d *Decoder) Buffered() io.Reader
- func (d *Decoder) Decode(out any) error
- func (d *Decoder) DisallowUnknownFields()
- func (d *Decoder) InputOffset() int64
- func (d *Decoder) More() bool
- func (d *Decoder) Token() (Token, error)
- func (d *Decoder) UseNumber()
- func (d *Decoder) Value() (Value, error)
- type Delim
- type Doc
- type Encoder
- type InvalidUnmarshalError
- type Kind
- type MappedFile
- type Marshaler
- type MarshalerError
- type Options
- type Parser
- type RawMessage
- type SyntaxError
- type Token
- type UnmarshalTypeError
- type Unmarshaler
- type UnsupportedTypeError
- type UnsupportedValueError
- type Value
- func (v Value) All() iter.Seq2[int, Value]
- func (v Value) Bool() bool
- func (v Value) Decode(out any) error
- func (v Value) Exists() bool
- func (v Value) Float() float64
- func (v Value) ForEach(fn func(Value) bool)
- func (v Value) ForEachKey(fn func(string, Value) bool)
- func (v Value) Get(path ...string) Value
- func (v Value) Index(n int) Value
- func (v Value) Int() int64
- func (v Value) IsNull() bool
- func (v Value) Key(name string) Value
- func (v Value) Keys() iter.Seq[string]
- func (v Value) Kind() Kind
- func (v Value) Len() int
- func (v Value) Members() iter.Seq2[string, Value]
- func (v Value) Path(path string) Value
- func (v Value) Raw() []byte
- func (v Value) String() string
- func (v Value) StringNoCopy() string
- func (v Value) Time() time.Time
- func (v Value) Values() iter.Seq[Value]
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Fast = Options{SortMapKeys: true}
Fast gives up HTML escaping and UTF-8 validation.
Use it when the output is not going into a page and the strings are known to be valid UTF-8 — decoded from JSON, read from a UTF-8 database column, built from Go string literals. The output is identical to Std's for any input that meets those conditions, and differs for any that does not.
Map keys are still sorted. Not sorting them is a bigger change than a few percent: it makes the same value encode differently on successive calls, which is a different promise rather than a faster one. Set SortMapKeys to false deliberately if that is wanted.
var Std = Options{EscapeHTML: true, ValidateStrings: true, SortMapKeys: true}
Std matches encoding/json byte for byte. It is what the package-level Marshal uses.
Functions ¶
func AppendBool ¶ added in v0.4.0
AppendBool appends "true" or "false".
func AppendFloat ¶ added in v0.4.0
AppendFloat appends v in the shortest form that round-trips, as JSON requires. bits is 32 or 64.
func AppendInt ¶ added in v0.4.0
AppendInt, AppendUint, AppendFloat and AppendBool are the rest of what a generated encoder needs for scalar fields, and are the same code Marshal uses for them.
func AppendString ¶ added in v0.4.0
AppendString writes s as a quoted JSON string under opts, which is what a generated encoder needs for a string field. It is the same code Marshal uses, exported so that generated code cannot drift from it.
func AppendUint ¶ added in v0.4.0
AppendUint appends v in decimal.
func Compact ¶ added in v0.4.0
Compact appends the JSON in src to dst with insignificant whitespace removed.
Whitespace inside a string is significant and is kept; everything between tokens goes. Invalid input is reported and dst is left as it was, which is what encoding/json.Compact does — it validates as it copies.
func DeletePath ¶ added in v0.4.0
DeletePath returns data with the value at path removed, along with its key if it had one and the separating comma if it needed one.
A path that does not exist is not an error: the document comes back unchanged, which is what "make sure this is not there" should do.
func ForEachLine ¶ added in v0.4.0
ForEachLine calls fn for each JSON value in data.
Whitespace between values, including the newlines, is skipped, so this reads NDJSON and equally a file of values with no separators at all. Input that is not valid JSON stops the walk and is returned as an error carrying the offset.
fn returning false stops the walk without an error, the same way Value.ForEach does.
The Value passed to fn is only valid for the duration of the call: it points into a batch that is reused for the values after it, which is what keeps this to a fixed amount of memory however long the input is.
func ForEachLineReader ¶ added in v0.4.0
ForEachLineReader is ForEachLine over a stream.
Memory is one batch plus the index over it, so a file larger than memory is fine: ten gigabytes of NDJSON goes through this in under twenty megabytes.
func ForEachLineReaderParallel ¶ added in v0.4.0
ForEachLineReaderParallel is ForEachLineReader across several goroutines.
fn is called on the calling goroutine, once per record, in input order. The parallelism is in the indexing, not in the callback, so fn needs no locking and sees records in the order they appeared.
The Value passed to fn is valid for the duration of the call.
Memory is bounded by the number of workers times the chunk size, whatever the length of the input.
An error stops the reader, drains the workers and is returned; so does fn returning false, without an error. The records before an error in the same chunk are still delivered — they are still good, and dropping them would silently truncate at a chunk boundary. Errors carry the byte offset in the whole stream, not in the chunk they were found in.
func HTMLEscape ¶ added in v0.4.0
HTMLEscape appends src to dst with <, >, & and the two Unicode line terminators replaced by their \u escapes, so the result can go inside a <script> tag without ending it.
It does not parse. In well-formed JSON none of those five can appear outside a string literal, so replacing them wherever they occur is the same thing as replacing them inside strings, and encoding/json.HTMLEscape makes the same bet. Nothing here validates, which is also what encoding/json does.
func Indent ¶ added in v0.4.0
Indent appends the JSON in src to dst, one element per line, each nested level prefixed by one more copy of indent and every line by prefix.
Byte-for-byte what encoding/json.Indent produces, including the space after a colon and the empty object written as {} rather than opened and closed on two lines.
func Marshal ¶ added in v0.4.0
Marshal returns the JSON encoding of v.
It is encoding/json.Marshal's contract — the same escaping, the same tag handling, the same sorted map keys, the same treatment of nil — produced by an encoder compiled once per type rather than by walking reflect for every field. Where the two could differ they are held together by a differential fuzz test rather than by inspection.
func MarshalIndent ¶ added in v0.4.0
MarshalIndent is Marshal followed by Indent, minus the proof: the bytes between them are this package's own output, compact and valid by construction, so the grammar walk Indent runs over input from outside proves nothing here. The masks are still built -- the writer lays out strings and depth from them -- and the walk was a fifth of the total.
func MarshalTo ¶ added in v0.4.0
MarshalTo appends the JSON encoding of v to dst and returns the extended slice.
The shape a server wants: one buffer, reused across responses, so encoding a stream of payloads does not allocate a new one for each.
func MarshalWrite ¶ added in v0.4.0
MarshalWrite writes the JSON encoding of v to w, matching encoding/json.
func RegisterEncoder ¶ added in v0.4.0
func RegisterEncoder[T any](fn AppendFunc)
RegisterEncoder installs fn as the encoder for T, for Marshal and everything built on it -- including T appearing as a struct field, a slice element or a map value.
It must be called before the first encode of a value containing T, which in practice means from an init function in the package that owns the generated code. Registering after a type has been encoded once has no effect: the compiled encoder for it is already cached, and so is every encoder that has already inlined a reference to it.
Registering the same type twice replaces the earlier registration.
func SetPath ¶ added in v0.4.0
SetPath returns data with the value at path replaced by v, encoding v with Marshal.
Missing structure is created: setting `a.b.c` on `{}` gives `{"a":{"b":{"c":...}}}`. A numeric component creates an array only if it is 0 or the path already leads to an array; sjson pads with nulls for a larger index and that is a footgun rather than a feature, so an index past the end of an existing array appends instead.
The path grammar is Value.Path's, minus the wildcards: `*` and `?` have no single answer to write to, so a path containing either is an error.
func SetRawPath ¶ added in v0.4.0
SetRawPath is SetPath with the replacement given as JSON text rather than a Go value.
raw is validated before it is spliced in, because a Set that produces a document which no longer parses is worse than an error.
func Skip ¶ added in v0.4.0
Skip returns the extent of the first JSON value in data: the offset of its first byte and the offset one past its last.
Validate and locate in one call, which sonic exposes as decoder.Skip. It is the operation behind "give me this value's bytes without decoding it" — a router picking one field out of a body, a proxy forwarding a subtree, a test comparing raw JSON.
ok is false if data holds no complete valid value. Trailing bytes after the first value are not an error and not included: `{} garbage` gives 0, 2, true.
Types ¶
type AppendFunc ¶ added in v0.4.0
AppendFunc writes v as JSON to the end of dst and returns the extended buffer. p points at a value of the registered type.
The contract is exact and unforgiving, because nothing checks it at run time: the bytes written must be the bytes Marshal would have written for the same value under the same Options, including the escaping and the field order. A registered encoder that disagrees produces wrong output with no error, so whatever writes one owes a differential test against Marshal.
type Decoder ¶ added in v0.4.0
type Decoder struct {
// contains filtered or unexported fields
}
A Decoder reads JSON values from a stream, one call to Decode per value.
Values may be separated by whitespace or by nothing at all; newline-delimited JSON is the case where they are separated by exactly one newline, and needs no special handling here.
func NewDecoder ¶ added in v0.4.0
NewDecoder returns a Decoder reading from r.
It may read more from r than it needs to answer a call to Decode; whatever it has read and not used is available from Decoder.Buffered.
func (*Decoder) Buffered ¶ added in v0.4.0
Buffered returns a reader over the bytes read from the underlying reader and not yet consumed by Decode.
func (*Decoder) Decode ¶ added in v0.4.0
Decode reads the next JSON value from the stream and stores it in v.
It returns io.EOF when the stream holds no further value, which is what ends a read loop.
func (*Decoder) DisallowUnknownFields ¶ added in v0.4.0
func (d *Decoder) DisallowUnknownFields()
DisallowUnknownFields makes Decode report an error when the input names a field the destination struct does not.
func (*Decoder) InputOffset ¶ added in v0.4.0
InputOffset returns the position in the stream just after the most recently decoded value.
func (*Decoder) More ¶ added in v0.4.0
More reports whether there is another element in the array or object being read, or another value in the stream.
func (*Decoder) Token ¶ added in v0.4.0
Token returns the next syntactic element: a Delim for a bracket, or the value of a string, number, bool or null.
Object keys come back as strings, in the position they appear. Commas and colons are consumed and never returned, which is what makes the token stream the same shape as encoding/json's.
It returns io.EOF when the input is exhausted. Token and Decoder.Decode interleave: after Token has returned the opening bracket of an array, Decode reads the next element of it, which is the whole point.
func (*Decoder) UseNumber ¶ added in v0.4.0
func (d *Decoder) UseNumber()
UseNumber makes Decode store a number in an any as a Number -- the digits as they were written -- rather than a float64, which cannot hold all of them.
func (*Decoder) Value ¶ added in v0.4.0
Value returns the next value in the stream without decoding it into a Go value.
The same framing as Decoder.Decode -- separators consumed, batches reused -- stopping one step earlier: the value is handed back as a Value pointing into the batch rather than copied into a destination. That is what makes reading line-delimited JSON cheap, because most records in a log are read to pull two fields out of them and decoding the other twenty is waste.
The value is validated before it is returned, unlike Scan, because a caller stepping through a stream is asking "is this a record" and the answer has to mean something. That costs about a third of the throughput and is not optional.
The returned Value is only valid until the next call. It points into a buffer this Decoder reuses, and reusing it is the whole reason a ten gigabyte stream fits in twenty megabytes.
It returns io.EOF when the input is exhausted.
type Doc ¶
type Doc struct {
// contains filtered or unexported fields
}
Doc is a parsed document. It holds the input and its structural index; no values are decoded until they are asked for.
func MustParse ¶ added in v0.4.0
MustParse is Parse for input already known to be valid. It panics if it is not.
For tests, for constants compiled into the program, and for the top of a function that has already validated its input. fastjson exposes the same thing and for the same reason: an error return that can never fire is noise at the call site.
func Parse ¶
Parse indexes data and validates its structure.
The returned Doc keeps data — it is not copied, and every string a Value yields points into it unless the string contains an escape.
Example (StringsHideStructure) ¶
Structure inside a string is text, which is the whole difficulty of stage one and is handled before any of it is interpreted.
package main
import (
"fmt"
"github.com/sebishogun/simdjson"
)
func main() {
doc, err := simdjson.Parse([]byte(`{"a":"},{\"b\":2},[","c":1}`))
if err != nil {
fmt.Println("err:", err)
return
}
fmt.Printf("%q\n", doc.Get("a").String())
fmt.Println(doc.Get("c").Int())
}
Output: "},{\"b\":2},[" 1
func Scan ¶
Scan indexes data without validating it.
Parse walks the whole document and checks every value against JSON's grammar, which is what makes it safe for input you did not produce — and it is most of the cost. If the goal is three fields out of a payload your own service just serialised, validating the other nine thousand is work nobody asked for.
Scan skips it. The structural index is still built, so navigation works exactly as it does after Parse; what is gone is the recursive descent that proves the parts you never look at are well-formed.
What that costs ¶
Malformed input gives wrong answers rather than errors. A missing colon, a trailing comma, a number like 10., an invalid escape — all are accepted, and the values around them may come back wrong or absent instead of failing. The index itself is still consistent, so nothing reads out of bounds and nothing panics; the result is simply not to be trusted.
Two things are still checked, because the index cannot be built without them: every string is terminated, and quotes balance. A document that fails either is rejected here too.
Use Parse for anything from outside. Use Scan when you produced the bytes.
func (*Doc) Get ¶
Get walks a path of object keys and returns the value at the end.
A missing key, or a path that runs into a non-object, yields an Invalid Value rather than an error — chaining is the common case and an error at every step would be unusable. Check Value.Exists.
type Encoder ¶ added in v0.4.0
type Encoder struct {
// contains filtered or unexported fields
}
An Encoder writes JSON values to a stream, one call to Encode per value, each followed by a newline.
func NewEncoder ¶ added in v0.4.0
NewEncoder returns an Encoder writing to w, matching encoding/json's defaults: HTML characters escaped, strings checked for valid UTF-8.
func (*Encoder) Encode ¶ added in v0.4.0
Encode writes the JSON encoding of v to the stream, followed by a newline.
func (*Encoder) Options ¶ added in v0.4.0
Options sets the whole option set at once, which is how the non-validating mode is reached from a stream. See Options.
func (*Encoder) SetEscapeHTML ¶ added in v0.4.0
SetEscapeHTML controls whether <, > and & are escaped. It is on by default.
type InvalidUnmarshalError ¶ added in v0.4.0
type InvalidUnmarshalError = json.InvalidUnmarshalError
InvalidUnmarshalError describes an invalid argument passed to Unmarshal — the argument must be a non-nil pointer.
type MappedFile ¶ added in v0.4.0
type MappedFile struct {
// contains filtered or unexported fields
}
A MappedFile is a JSON document mapped into memory.
Close must be called, and no Value taken from the document may be used afterwards: the bytes go away with the mapping, and reading them then is a segmentation fault rather than a Go panic. Value.String copies, so a string taken from it outlives Close; Value.StringNoCopy and Value.Raw do not.
func OpenFile ¶ added in v0.4.0
func OpenFile(path string, validate bool) (*MappedFile, error)
OpenFile maps path into memory and indexes it.
validate says whether to prove the whole document well-formed, which is the difference between Parse and Scan: validating a two gigabyte file costs about four times what indexing it does, and a caller pulling one field out of a log does not need it.
func (*MappedFile) Bytes ¶ added in v0.4.0
func (m *MappedFile) Bytes() []byte
Bytes returns the mapped file's contents. It is valid until MappedFile.Close, and writing to it will fault: the mapping is read-only.
func (*MappedFile) Close ¶ added in v0.4.0
func (m *MappedFile) Close() error
Close unmaps the file and closes it.
func (*MappedFile) Doc ¶ added in v0.4.0
func (m *MappedFile) Doc() *Doc
Doc returns the parsed document. It is valid until MappedFile.Close.
type Marshaler ¶ added in v0.4.0
Marshaler is the interface implemented by types that can marshal themselves into valid JSON.
It is an alias for json.Marshaler.
type MarshalerError ¶ added in v0.4.0
type MarshalerError = json.MarshalerError
MarshalerError is returned when a type's own MarshalJSON or MarshalText method returns an error.
type Options ¶ added in v0.4.0
type Options struct {
// EscapeHTML writes `<`, `>` and `&` as <, > and &, and
// rewrites U+2028 and U+2029, so the output can be embedded in an HTML
// document without becoming script. encoding/json does this by default and
// so does this package.
//
// Turning it off is worth a few percent and is safe only if the output
// never reaches a page. Note that some other libraries have it off by
// default, which is worth knowing when comparing their numbers.
EscapeHTML bool
// ValidateStrings replaces bytes that are not valid UTF-8 with U+FFFD,
// which is what encoding/json does. Off, they are written through as-is,
// producing output that is not valid JSON if the input was not valid UTF-8.
//
// This is the expensive one — on a document of non-ASCII text, validation
// is about a third of the encode — and the right choice when the strings
// come from somewhere that already guarantees UTF-8.
ValidateStrings bool
// SortMapKeys writes a map's keys in order. encoding/json always does, so
// this is on in [Std] and every byte-for-byte comparison depends on it.
//
// Off, keys come out in whatever order the map iterates, which Go
// deliberately randomises — so the same map encodes differently on
// successive calls. That is fine for a payload nobody diffs and fatal for
// a cache key, an ETag or a signature. encoding/json/v2 makes it opt-in;
// this keeps v1's default and lets you turn it off, which is the safer way
// round.
SortMapKeys bool
// OmitZeroStructFields drops every struct field holding its type's zero
// value, as though each carried `omitzero`.
//
// New in encoding/json/v2 as an option, and useful for the case the tag
// cannot serve: a type from another package, or a struct being encoded for
// a wire format that treats absent and zero the same.
//
// It follows `omitzero` and not `omitempty`: an empty slice and an empty
// map are their zero value only when nil, and a type with its own IsZero
// method is asked. A field with an explicit tag keeps whatever the tag
// said.
OmitZeroStructFields bool
}
Options selects what an encoder checks and escapes.
The defaults match encoding/json exactly, because a drop-in replacement that quietly produces different bytes is worse than a slow one. Everything here is a way to buy speed by giving something up, and each says what.
func (Options) Marshal ¶ added in v0.4.0
Marshal returns the JSON encoding of v under these options.
func (Options) MarshalTo ¶ added in v0.4.0
MarshalTo appends the JSON encoding of v to dst under these options.
func (Options) MarshalWrite ¶ added in v0.4.0
MarshalWrite writes the JSON encoding of v to w.
The shape encoding/json/v2 added as MarshalWrite: encode straight into the destination rather than building a []byte and handing it over. For a large value going to a socket or a file this is the difference between one buffer and two.
It is not Encoder.Encode: that appends a newline, because it is for writing a stream of values. This writes exactly the value.
type Parser ¶
type Parser struct {
// contains filtered or unexported fields
}
Parser parses documents, reusing its index buffers between them.
A server handling many payloads should keep one per goroutine: Parse allocates a fresh index each time, and for a document of a few hundred kilobytes that index is several times the size of the document itself. A Parser reuses it, so the second and later documents allocate almost nothing.
A Parser is not safe for concurrent use.
Example ¶
A Parser reuses its index between documents, which is what a server handling a stream of payloads wants. The Doc it returns is only valid until the next Parse on the same Parser.
package main
import (
"fmt"
"github.com/sebishogun/simdjson"
)
func main() {
var p simdjson.Parser
for _, payload := range [][]byte{
[]byte(`{"id":1}`),
[]byte(`{"id":2}`),
} {
doc, err := p.Parse(payload)
if err != nil {
return
}
fmt.Println(doc.Get("id").Int())
}
}
Output: 1 2
type RawMessage ¶ added in v0.4.0
type RawMessage = json.RawMessage
RawMessage is a raw encoded JSON value. It implements json.Marshaler and json.Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding.
It is an alias for json.RawMessage, so the two are the same type.
type SyntaxError ¶ added in v0.4.0
type SyntaxError struct {
Offset int64
// contains filtered or unexported fields
}
A SyntaxError reports that the input is not valid JSON.
Offset is the byte in the input where the problem was found, which is the whole reason this is a type and not a string: the position used to be formatted into the message and a caller who wanted it had to parse English back out. encoding/json, encoding/json/v2, goccy and sonic all carry it; fastjson and minio/simdjson-go do not, and that is the wrong side to be on.
It is deliberately shaped like json.SyntaxError — same field, same meaning, same Error() text apart from the package name — but it cannot be an alias, because json.SyntaxError's msg field is unexported and one cannot be built from outside that package.
func (*SyntaxError) Error ¶ added in v0.4.0
func (e *SyntaxError) Error() string
type Token ¶ added in v0.4.0
A Token is a delimiter, a string, a number, a bool, or nil — the same set encoding/json.Token holds, and the same types, so code written against one works against the other.
type UnmarshalTypeError ¶ added in v0.4.0
type UnmarshalTypeError = json.UnmarshalTypeError
UnmarshalTypeError describes a JSON value that was not appropriate for a value of a specific Go type. Its Offset field is the byte offset in the input after reading the value.
type Unmarshaler ¶ added in v0.4.0
type Unmarshaler = json.Unmarshaler
Unmarshaler is the interface implemented by types that can unmarshal a JSON description of themselves.
It is an alias for json.Unmarshaler.
type UnsupportedTypeError ¶ added in v0.4.0
type UnsupportedTypeError = json.UnsupportedTypeError
UnsupportedTypeError is returned by Marshal for a Go type that cannot be represented as JSON.
type UnsupportedValueError ¶ added in v0.4.0
type UnsupportedValueError = json.UnsupportedValueError
UnsupportedValueError is returned by Marshal for a value that cannot be represented as JSON — an infinity or a NaN.
type Value ¶
type Value struct {
// contains filtered or unexported fields
}
Value is one JSON value inside a document.
func GetMany ¶ added in v0.4.0
GetMany returns the values at each of paths, in order.
One index, many lookups. gjson's GetMany is documented as one pass over the document for N paths, which is what it has to do because it has no index; here the document is scanned once whatever N is, and each path after that is a walk over structural positions. So the second path is nearly free and the hundredth is too.
A path that does not exist gives an Invalid Value in that position rather than an error, matching gjson. A document that does not parse gives all-Invalid.
Each path is a sequence of object keys. For anything more than that — array indices, wildcards, queries — walk with Value.Index and Value.ForEach.
func GetPath ¶ added in v0.4.0
GetPath indexes data and returns the value at path.
It does not validate the whole document, only the part it walks through — the same contract gjson.Get has, and for the same reason: a caller pulling one field out of a payload is not asking whether the other fields are well-formed, and proving it costs four times what finding the field does. Use Parse when the answer matters.
For more than one query on the same document, index once with Parser.Scan or Parse and use Doc.Path. That is the whole point of having an index and it is where this stops being a straight loss against gjson: gjson keeps nothing, so its second query costs exactly what its first did.
one field, near the front gjson 105 us this 83 us one field, near the back gjson 105 us this 85 us ten fields gjson 634 us this 239 us
It returns no error: a document that does not parse gives an Invalid Value, the same as a path that does not exist. Value.Exists tells them apart from a value that is there.
func (Value) All ¶ added in v0.4.0
All ranges over the elements of an array, or over nothing for any other kind.
The range form of Value.ForEach, for `for i, e := range v.All()`.
func (Value) Decode ¶ added in v0.4.0
Decode stores this value in the value pointed to by v.
It is Unmarshal for a part of a document, so a large payload can be navigated to the field that matters and only that field decoded.
func (Value) Exists ¶
Exists reports whether the value was found.
Example ¶
A missing key yields a Value that does not exist rather than an error, so a path can be walked without checking every step.
package main
import (
"fmt"
"github.com/sebishogun/simdjson"
)
func main() {
doc, _ := simdjson.Parse([]byte(`{"a":{"b":1}}`))
fmt.Println(doc.Get("a", "b").Exists())
fmt.Println(doc.Get("a", "zzz").Exists())
fmt.Println(doc.Get("nope", "deeper").Exists())
}
Output: true false false
func (Value) ForEach ¶
ForEach calls fn for each element of an array until it returns false.
Example ¶
Iterating an array without building one.
package main
import (
"fmt"
"github.com/sebishogun/simdjson"
)
func main() {
doc, _ := simdjson.Parse([]byte(`{"scores":[10,20,30]}`))
total := int64(0)
doc.Get("scores").ForEach(func(v simdjson.Value) bool {
total += v.Int()
return true
})
fmt.Println(total)
}
Output: 60
func (Value) ForEachKey ¶
ForEachKey calls fn for each field of an object until it returns false.
Example ¶
Iterating an object's fields.
package main
import (
"fmt"
"github.com/sebishogun/simdjson"
)
func main() {
doc, _ := simdjson.Parse([]byte(`{"a":1,"b":2}`))
doc.Root().ForEachKey(func(k string, v simdjson.Value) bool {
fmt.Printf("%s=%d\n", k, v.Int())
return true
})
}
Output: a=1 b=2
func (Value) Get ¶ added in v0.4.0
Get returns the value at a path relative to v.
The same walk as Doc.Get but starting here, which is what makes a Value worth holding on to: find the interesting subtree once, then ask it questions. gjson's Result.Get is the same idea.
A path that does not exist gives an Invalid Value; see Value.Exists.
func (Value) Key ¶
Key returns the value of a field in an object.
The scan walks the object's structural entries rather than its bytes, so passing over a large nested value costs one bracket match instead of a parse. A missing key gives an Invalid Value; see Value.Exists.
func (Value) Members ¶ added in v0.4.0
Members ranges over the fields of an object, or over nothing for any other kind.
The range form of Value.ForEachKey. It is not called All because an object and an array are different shapes and returning the same type for both would mean an index nobody wants or a key that does not exist.
func (Value) Path ¶ added in v0.4.0
Path returns the value at a dot-separated path, relative to v.
A component that does not exist gives an Invalid Value; see Value.Exists.
func (Value) String ¶
String returns a string value's contents, or "" for anything else.
A string with no escape is returned without copying the bytes out of the document; one with an escape is decoded into a new string.
func (Value) StringNoCopy ¶ added in v0.4.0
StringNoCopy is Value.String without the copy: for a string that needs no unescaping, the result points into the document rather than at bytes of its own.
The whole point of a two-stage parser is that the bytes are already there and already known to be a string, so copying them out is work nobody asked for. minio/simdjson-go exposes the same thing as WithCopyStrings(false), fastjson as StringBytes, and gjson's Result.Raw is a substring of the input by construction.
The cost is a lifetime the compiler will not check for you. The returned string aliases the slice passed to Parse, so it is only valid while that slice is unmodified and reachable, and writing through the original slice changes a string — which Go otherwise guarantees cannot happen. Use it when the document outlives the strings taken from it and both stay in one function; use Value.String anywhere the string escapes.
A string containing an escape sequence has nothing to alias, because its decoded form is not present in the document. Those are unescaped and copied exactly as Value.String does, so this is never wrong, only sometimes no faster.
Source Files
¶
- api.go
- compact_parallel.go
- compat.go
- decoder.go
- encode_any.go
- encode_parallel.go
- errcontext.go
- escape.go
- fielddominance.go
- float_fast.go
- float_pow10_gen.go
- indent_parallel.go
- itoa.go
- lines.go
- lines_par.go
- marshal.go
- mmap_unix.go
- options.go
- parallel_index.go
- parse_parallel.go
- path.go
- pow10_table.go
- register.go
- scan.go
- schubfach.go
- set.go
- simdjson.go
- sortmap.go
- stream.go
- stream_parallel.go
- stream_prefetch.go
- structural.go
- token.go
- unmarshal.go
- unmarshal_parallel.go
- valid.go
- valid_parallel.go
- validate.go
- value.go
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
gentest
Package gentest is the fixture structgen is exercised against.
|
Package gentest is the fixture structgen is exercised against. |
|
stdlibtest
The random-document generator encoding/json's own tests build jsonBig with (scanner_test.go, BSD-3, The Go Authors), reproduced for the vendored decode tests.
|
The random-document generator encoding/json's own tests build jsonBig with (scanner_test.go, BSD-3, The Go Authors), reproduced for the vendored decode tests. |