Documentation
¶
Overview ¶
Package json is a pure-Go (CGO-free) reimplementation of Ruby's JSON parser and generator, matching MRI 4.0.5's JSON.parse / JSON.generate / JSON.pretty_generate byte-for-byte. Parsing and generating JSON is fully deterministic — it needs no interpreter — so it lives here as pure Go: Parse turns a JSON document into a tree of Ruby values, and Generate / PrettyGenerate render such a tree back to the exact bytes MRI's json gem produces.
It is the JSON backend for go-embedded-ruby, but is a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-regexp (the Onigmo engine), go-ruby-erb (the ERB compiler) and go-ruby-yaml (Psych).
Ruby value model ¶
A Ruby value is represented by an [any] drawn from a small, fixed set of Go types so a host (such as go-embedded-ruby) can map its own object graph to and from this package:
Ruby Go (Generate accepts) Go (Parse returns) ---- --------------------- ------------------ nil nil nil true / false bool bool Integer int, int64, *big.Int int64 or *big.Int Float float64, float32 float64 String string string Symbol Symbol Symbol (symbolize_names) Array []any []any Hash *Map (ordered), map[...]any *Map (insertion order)
Parse returns mappings as an ordered *Map so key order is preserved (MRI keeps it); Generate accepts a *Map, or a plain Go map (emitted in sorted-key order for determinism).
Index ¶
- func Generate(v Value, opts ...Option) (string, error)
- func GenerateSource(src Source, opts ...Option) (string, error)
- func ParseInto(s string, dst Builder, opts ...Option) error
- func PrettyGenerate(v Value, opts ...Option) (string, error)
- func PrettyGenerateSource(src Source, opts ...Option) (string, error)
- type Builder
- type Encoder
- func (e *Encoder) Array(n int, emit func() error) error
- func (e *Encoder) Big(n *big.Int)
- func (e *Encoder) Bool(b bool)
- func (e *Encoder) Elem()
- func (e *Encoder) Float(f float64) error
- func (e *Encoder) Int(n int64)
- func (e *Encoder) Key(s string)
- func (e *Encoder) Null()
- func (e *Encoder) Object(n int, emit func() error) error
- func (e *Encoder) Str(s string)
- type Error
- type GeneratorError
- type Map
- type NestingError
- type Option
- type Pair
- type ParserError
- type Source
- type Symbol
- type TypeError
- type Value
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Generate ¶
Generate renders a Ruby value to a compact JSON document, matching JSON.generate / Object#to_json. The value is drawn from the package value model; a non-finite float without WithAllowNaN returns a *GeneratorError, and over-deep nesting a *NestingError.
func GenerateSource ¶
GenerateSource renders src to a compact JSON document by pulling its values through an Encoder, the streaming counterpart of Generate (no intermediate value tree). The options behave exactly as for Generate.
func ParseInto ¶
ParseInto parses a JSON document straight into the host Builder dst, reproducing JSON.parse semantics (and its errors) but with no intermediate tree of this package's values — the parser drives dst as it reads, so a host such as go-embedded-ruby materialises its own object graph in a single allocation-light pass. On success dst.Result() holds the top-level value; on a malformed document, nesting overflow or non-string input the matching error is returned and dst's result is unspecified.
func PrettyGenerate ¶
PrettyGenerate renders a Ruby value with MRI's JSON.pretty_generate layout: two-space indent, "\n" object/array newlines and a single space after ':'. Any WithIndent / WithSpace / WithObjectNL / WithArrayNL options override those defaults.
func PrettyGenerateSource ¶
PrettyGenerateSource is GenerateSource with MRI's pretty_generate layout (two-space indent, "\n" newlines, a space after ':'), overridable by opts.
Types ¶
type Builder ¶
type Builder interface {
// Null appends a JSON null.
Null()
// Bool appends a JSON true/false.
Bool(b bool)
// Int appends an integer that fits in int64.
Int(n int64)
// Big appends an integer too large for int64 (never nil).
Big(n *big.Int)
// Float appends a floating-point number.
Float(f float64)
// Str appends a string value (an object value or array element, never a key).
Str(s string)
// BeginArray opens an array; n is a capacity hint for pre-allocation, or 0
// when the count is not known up front (the default parser passes 0 and sizes
// each container exactly at EndArray instead of pre-scanning for the count). A
// Builder that pre-sizes must tolerate n == 0 by growing on demand. The
// emitted values up to the matching EndArray are its elements.
BeginArray(n int)
// EndArray closes the array opened by the matching BeginArray.
EndArray()
// BeginObject opens an object; n is a capacity hint, or 0 when unknown (see
// BeginArray). It is followed by (Key, value) sequences up to EndObject.
BeginObject(n int)
// Key sets the key for the next emitted value. symbolize reports whether the
// host should intern it as a Symbol (MRI's symbolize_names: true).
Key(s string, symbolize bool)
// EndObject closes the object opened by the matching BeginObject.
EndObject()
// Result returns the single top-level value once parsing completes.
Result() Value
}
Builder is a streaming sink the parser drives as it reads a document, so a host (such as go-embedded-ruby) can construct its own object graph directly — with no intermediate tree of this package's `any` values, and no second conversion pass. ParseInto feeds a Builder; Parse is ParseInto over the package's own [valueBuilder].
The parser calls the scalar methods (Null/Bool/Int/Big/Float/Str) for leaf values and the container methods (BeginArray/EndArray and BeginObject/Key/ EndObject) around composites. Every emitted value — a scalar, or the array or object just closed — becomes the *current value*; the host attaches it to its enclosing container (the most recent open array element, or the value of the most recent object Key) on the next call, or returns it from Builder.Result at the top level. The call sequence is well-formed by construction: containers nest correctly, an object alternates Key then value, and exactly one value is emitted at the top level.
A Builder that pre-sizes its containers (BeginArray/BeginObject carry the element count) and interns small scalars turns parsing into a single allocation-light pass.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder is the generate-side façade a Source writes through. It wraps the streaming generator, so a host emits a value with one call and the bytes, nesting limit, MRI-faithful formatting and pretty-print layout are handled here.
func (*Encoder) Array ¶
Array emits a JSON array of n elements (n is the exact count, used only for the array_nl / indent layout decisions — it is not relied on for correctness). emit is called once and must push exactly n element values via the encoder; it honours the configured nesting limit and pretty-print layout. A nil emit (or n == 0) writes an empty array.
func (*Encoder) Elem ¶
func (e *Encoder) Elem()
Elem must be called once per array element by the Array emit callback, before emitting that element, so the comma and newline separators (and the per-line indent) land between elements exactly as MRI lays them out.
func (*Encoder) Float ¶
Float emits a float with MRI's fpconv layout, returning a GeneratorError for a non-finite value unless allow_nan is configured.
func (*Encoder) Key ¶
Key emits an object key (the pair separator, indent, the quoted key and the space/colon) before its value, and must be called once per pair by the Object emit callback, immediately before emitting that pair's value. The key is rendered as a JSON string (MRI coerces every object key to a string).
func (*Encoder) Object ¶
Object emits a JSON object of n pairs (n is the exact count, used only for the object_nl / indent layout). emit is called once and must push exactly n pairs via Encoder.Key then a value. A nil emit (or n == 0) writes an empty object.
type Error ¶
type Error interface {
error
// RubyClass is the fully-qualified Ruby exception class this error maps to
// (e.g. "JSON::ParserError").
RubyClass() string
}
Error is the base of this package's error taxonomy, mirroring MRI's JSON::JSONError hierarchy. Every error returned by Parse, Generate and PrettyGenerate is one of *ParserError, *NestingError, *GeneratorError or *TypeError; all satisfy Error. RubyClass reports the matching Ruby exception class name so a host (go-embedded-ruby) can raise the right Ruby exception.
type GeneratorError ¶
type GeneratorError struct{ Message string }
GeneratorError is raised when a value cannot be generated — a non-finite float without allow_nan: MRI's JSON::GeneratorError.
func (*GeneratorError) Error ¶
func (e *GeneratorError) Error() string
func (*GeneratorError) RubyClass ¶
func (e *GeneratorError) RubyClass() string
type Map ¶
type Map struct {
// contains filtered or unexported fields
}
Map is an insertion-ordered Ruby Hash. Parse returns objects as *Map so key order round-trips; Generate accepts *Map, or a plain Go map (emitted in sorted key order).
The identity index is built lazily: while a Map holds at most [mapIndexThreshold] pairs a linear scan resolves a key faster than a hash lookup and needs no map allocation, so the overwhelmingly common small object carries no per-object map. The index is materialised only once a Map grows past the threshold, restoring O(1) lookup for large hashes.
type NestingError ¶
type NestingError struct{ Message string }
NestingError is raised when nesting exceeds the limit, on both parse and generate: MRI's JSON::NestingError (a subclass of JSON::ParserError in MRI; we model it as its own type and report it as the more specific class).
func (*NestingError) Error ¶
func (e *NestingError) Error() string
func (*NestingError) RubyClass ¶
func (e *NestingError) RubyClass() string
type Option ¶
type Option func(*config)
Option configures Parse, Generate and PrettyGenerate, mirroring the keyword options of MRI's JSON.parse / JSON.generate / JSON.pretty_generate. An option that does not apply to a given call is simply ignored (MRI tolerates the same).
func WithAllowNaN ¶
WithAllowNaN permits the non-finite floats NaN, Infinity and -Infinity in both directions (MRI's allow_nan:). On Generate they emit the bare NaN / Infinity / -Infinity tokens; on Parse those bare tokens are accepted. Without it, a non-finite float is a GeneratorError and the tokens are a ParserError.
func WithArrayNL ¶
WithArrayNL sets the newline string emitted after each array delimiter and between elements (MRI's array_nl:). PrettyGenerate defaults it to "\n".
func WithIndent ¶
WithIndent sets the per-level indent string used by generation (MRI's indent:). PrettyGenerate defaults it to two spaces.
func WithMaxNesting ¶
WithMaxNesting sets the maximum structure depth for Parse and Generate (MRI's max_nesting:). A value of 0 (or negative) disables the limit, matching MRI's max_nesting: false / 0.
func WithObjectNL ¶
WithObjectNL sets the newline string emitted after each object delimiter and between pairs (MRI's object_nl:). PrettyGenerate defaults it to "\n".
func WithSpace ¶
WithSpace sets the string emitted after the ':' separating an object key from its value (MRI's space:). PrettyGenerate defaults it to a single space.
func WithSpaceBefore ¶
WithSpaceBefore sets the string emitted before the ':' in an object pair (MRI's space_before:). Default empty.
func WithSymbolizeNames ¶
WithSymbolizeNames makes Parse return object keys as Symbol instead of string (MRI's symbolize_names: true). It has no effect on Generate.
type ParserError ¶
type ParserError struct{ Message string }
ParserError is a malformed-document error: MRI's JSON::ParserError. Its message matches MRI's, including the "at line L column C" suffix.
func (*ParserError) Error ¶
func (e *ParserError) Error() string
func (*ParserError) RubyClass ¶
func (e *ParserError) RubyClass() string
type Source ¶
Source is a value a host can render to JSON by pushing it into an Encoder, the mirror of Builder on the generate side: instead of first converting its object graph into this package's value model and handing that to Generate, the host walks its own graph once and calls the Encoder's typed methods. This removes the per-node intermediate value and its type-switch.
GenerateSource / PrettyGenerateSource call EmitTo with a configured Encoder; EmitTo emits exactly one value (a scalar, or one Array/Object whose body emits its elements). A non-nil error (e.g. a non-finite float without allow_nan, surfaced by Encoder.Float) aborts generation and is returned to the caller.
type Symbol ¶
type Symbol string
Symbol is a Ruby Symbol (`:name`). Generate emits it as its bare name (a JSON string of the name); Parse yields Symbol keys when WithSymbolizeNames(true) is set (MRI's symbolize_names: true).
type TypeError ¶
type TypeError struct{ Message string }
TypeError mirrors MRI raising a plain TypeError when Parse is handed a non-String (e.g. JSON.parse(123)).
type Value ¶
type Value = any
Value is the interface satisfied by every Ruby value this package handles. It is purely documentary — the public API uses any — but a host may use it to constrain its own adapters.
func Parse ¶
Parse parses a JSON document into a tree of Ruby values, matching JSON.parse. Objects come back as an ordered *Map (key order preserved), arrays as []any, numbers as int64 / *big.Int / float64, and strings as Go strings (with WithSymbolizeNames, object keys come back as Symbol). A malformed document returns a *ParserError; exceeding the nesting limit a *NestingError; a non-string input a *TypeError.
