...

Package jwriter

import "github.com/launchdarkly/go-jsonstream/v3/jwriter"
Overview
Index
Examples

Overview ▾

Package jwriter provides an efficient mechanism for writing JSON data sequentially.

The high-level API for this package, Writer, is designed to facilitate writing custom JSON marshaling logic concisely and reliably. Output is buffered in memory.

import (
    "gopkg.in/launchdarkly/jsonstream.v1/jwriter"
)

type myStruct struct {
    value int
}

func (s myStruct) WriteToJSONWriter(w *jwriter.Writer) {
    obj := w.Object() // writing a JSON object structure like {"value":2}
    obj.Property("value").Int(s.value)
    obj.End()
}

func PrintMyStructJSON(s myStruct) {
    w := jwriter.NewWriter()
    s.WriteToJSONWriter(&w)
    fmt.Println(string(w.Bytes())
}

Output can optionally be dumped to an io.Writer at intervals to avoid allocating a large buffer:

func WriteToHTTPResponse(s myStruct, resp http.ResponseWriter) {
    resp.Header.Add("Content-Type", "application/json")
    w := jwriter.NewStreamingWriter(resp, 1000)
    myStruct.WriteToJSONWriter(&w)
}

The underlying low-level token writing mechanism has two available implementations. The default implementation has no external dependencies. For interoperability with the easyjson library (https://github.com/mailru/easyjson), there is also an implementation that delegates to the easyjson streaming writer; this is enabled by setting the build tag "launchdarkly_easyjson". Be aware that by default, easyjson uses Go's "unsafe" package (https://pkg.go.dev/unsafe), which may not be available on all platforms.

Setting the "launchdarkly_easyjson" tag also adds a new constructor function, NewWriterFromEasyJSONWriter, allowing Writer-based code to send output directly to an existing EasyJSON jwriter.Writer. This may be desirable in order to define common marshaling logic that may be used with or without EasyJSON. For example:

import (
    ej_jwriter "github.com/mailru/easyjson/jwriter"
)

func (s myStruct) MarshalEasyJSON(w *ej_jwriter.Writer) {
    ww := jwriter.NewWriterFromEasyJSONWriter(w)
    s.WriteToJSONWriter(&ww)
}

Example

Code:

w := NewWriter()

obj := w.Object()
obj.Name("propertyName").String("propertyValue")
obj.End()

if err := w.Error(); err != nil {
    fmt.Println("error:", err.Error())
} else {
    fmt.Println(string(w.Bytes()))
}

Output:

{"propertyName":"propertyValue"}

Index ▾

func MarshalJSONWithWriter(writable Writable) ([]byte, error)
type ArrayState
    func (arr *ArrayState) Array() ArrayState
    func (arr *ArrayState) Bool(value bool)
    func (arr *ArrayState) End()
    func (arr *ArrayState) Float64(value float64)
    func (arr *ArrayState) Int(value int)
    func (arr *ArrayState) Null()
    func (arr *ArrayState) Object() ObjectState
    func (arr *ArrayState) Raw(value json.RawMessage)
    func (arr *ArrayState) String(value string)
type ObjectState
    func (obj *ObjectState) End()
    func (obj *ObjectState) Maybe(name string, shouldWrite bool) *Writer
    func (obj *ObjectState) Name(name string) *Writer
type Writable
type Writer
    func NewStreamingWriter(target io.Writer, bufferSize int) Writer
    func NewWriter() Writer
    func (w *Writer) AddError(err error)
    func (w *Writer) Array() ArrayState
    func (w *Writer) Bool(value bool)
    func (w *Writer) BoolOrNull(isDefined bool, value bool)
    func (w *Writer) Bytes() []byte
    func (w *Writer) Error() error
    func (w *Writer) Float64(value float64)
    func (w *Writer) Float64OrNull(isDefined bool, value float64)
    func (w *Writer) Flush() error
    func (w *Writer) Int(value int)
    func (w *Writer) IntOrNull(isDefined bool, value int)
    func (w *Writer) Null()
    func (w *Writer) Object() ObjectState
    func (w *Writer) Raw(value json.RawMessage)
    func (w *Writer) String(value string)
    func (w *Writer) StringOrNull(isDefined bool, value string)

Package files

interfaces.go no_op_writer.go package_info.go streamable_buffer.go test_behavior_default.go token_writer_default.go writer.go writer_array.go writer_init_default.go writer_marshal.go writer_object.go

func MarshalJSONWithWriter

func MarshalJSONWithWriter(writable Writable) ([]byte, error)

MarshalJSONWithWriter is a convenience method for implementing json.Marshaler to marshal to a byte slice with the default TokenWriter implementation.

type ArrayState

ArrayState is a decorator that manages the state of a JSON array that is in the process of being written.

Calling Writer.Array() or ObjectState.Array() creates an ArrayState. Until ArrayState.End() is called, writing any value to either the ArrayState or the Writer will cause commas to be added between values as needed.

type ArrayState struct {
    // contains filtered or unexported fields
}

func (*ArrayState) Array

func (arr *ArrayState) Array() ArrayState

Array is equivalent to calling writer.Array(), to create a nested array.

Example

Code:

w := NewWriter()
arr := w.Array()
arr.Int(1)
subArr := arr.Array()
subArr.Int(2)
subArr.Int(3)
subArr.End()
arr.Int(4)
arr.End()

fmt.Println(string(w.Bytes()))

Output:

[1,[2,3],4]

func (*ArrayState) Bool

func (arr *ArrayState) Bool(value bool)

Bool is equivalent to writer.Bool(value).

Example

Code:

w := NewWriter()
arr := w.Array()
arr.Bool(true)
arr.Bool(false)
arr.End()

fmt.Println(string(w.Bytes()))

Output:

[true,false]

func (*ArrayState) End

func (arr *ArrayState) End()

End writes the closing delimiter of the array.

func (*ArrayState) Float64

func (arr *ArrayState) Float64(value float64)

Float64 is equivalent to writer.Float64(value).

Example

Code:

w := NewWriter()
arr := w.Array()
arr.Float64(1234.5)
arr.Float64(6)
arr.End()

fmt.Println(string(w.Bytes()))

Output:

[1234.5,6]

func (*ArrayState) Int

func (arr *ArrayState) Int(value int)

Int is equivalent to writer.Int(value).

Example

Code:

w := NewWriter()
arr := w.Array()
arr.Int(123)
arr.Int(456)
arr.End()

fmt.Println(string(w.Bytes()))

Output:

[123,456]

func (*ArrayState) Null

func (arr *ArrayState) Null()

Null is equivalent to writer.Null().

Example

Code:

w := NewWriter()
arr := w.Array()
arr.Null()
arr.Null()
arr.End()

fmt.Println(string(w.Bytes()))

Output:

[null,null]

func (*ArrayState) Object

func (arr *ArrayState) Object() ObjectState

Object is equivalent to calling writer.Object(), to create a nested object.

Example

Code:

w := NewWriter()
arr := w.Array()
obj1 := arr.Object()
obj1.Name("value").Int(1)
obj1.End()
obj2 := arr.Object()
obj2.Name("value").Int(2)
obj2.End()
arr.End()

fmt.Println(string(w.Bytes()))

Output:

[{"value":1},{"value":2}]

func (*ArrayState) Raw

func (arr *ArrayState) Raw(value json.RawMessage)

Raw is equivalent to calling writer.Raw().

Example

Code:

data := json.RawMessage(`{"value":1}`)
w := NewWriter()
arr := w.Array()
arr.Raw(data)
arr.End()

fmt.Println(string(w.Bytes()))

Output:

[{"value":1}]

func (*ArrayState) String

func (arr *ArrayState) String(value string)

String is equivalent to writer.String(value).

Example

Code:

w := NewWriter()
arr := w.Array()
arr.String(`string says "hello"`)
arr.String("ok")
arr.End()
fmt.Println(string(w.Bytes()))

Output:

["string says \"hello\"","ok"]

type ObjectState

ObjectState is a decorator that writes values to an underlying Writer within the context of a JSON object, adding property names and commas between values as appropriate.

type ObjectState struct {
    // contains filtered or unexported fields
}

func (*ObjectState) End

func (obj *ObjectState) End()

End writes the closing delimiter of the object.

func (*ObjectState) Maybe

func (obj *ObjectState) Maybe(name string, shouldWrite bool) *Writer

Maybe writes an object property name conditionally depending on a boolean parameter. If shouldWrite is true, this behaves the same as Property(name). However, if shouldWrite is false, it does not write a property name and instead of returning the underlying Writer, it returns a stub Writer that does not produce any output. This allows you to chain method calls without having to use an if statement.

obj.Maybe(shouldWeIncludeTheProperty, "myBooleanProperty").Bool(true)

Example

Code:

w := NewWriter()
obj := w.Object()
obj.Maybe("notPresent", false).Int(1)
obj.Maybe("present", true).Int(2)
obj.End()

fmt.Println(string(w.Bytes()))

Output:

{"present":2}

func (*ObjectState) Name

func (obj *ObjectState) Name(name string) *Writer

Name writes an object property name and a colon. You can then use Writer methods to write the property value. The return value is the same as the underlying Writer, so you can chain method calls:

obj.Name("myBooleanProperty").Bool(true)

Example

Code:

myCustomMarshaler := func(w *Writer) {
    subObject := w.Object()
    subObject.Name("yes").Bool(true)
    subObject.End()
}

w := NewWriter()

obj := w.Object()
myCustomMarshaler(obj.Name("subObject"))
obj.End()

fmt.Println(string(w.Bytes()))

Output:

{"subObject":{"yes":true}}

type Writable

Writable is an interface for types that can write their data to a Writer.

type Writable interface {
    // WriteToJSONWriter writes JSON content to the Writer.
    //
    // This method does not need to return an error value. If the Writer encounters an error during output
    // generation, it will remember its own error state, which can be detected with Writer.Error().
    WriteToJSONWriter(*Writer)
}

type Writer

Writer is a high-level API for writing JSON data sequentially.

It is designed to make writing custom marshallers for application types as convenient as possible. The general usage pattern is as follows:

- There is one method for each JSON data type.

- For writing array or object structures, the Array and Object methods return a struct that keeps track of additional writer state while that structure is being written.

- If any method encounters an error (for instance, if an underlying io.Writer returns an error when using NewStreamingWriter), or if an error is explicitly raised with AddError, the Writer permanently enters a failed state and remembers that error; all subsequent method calls for producing output will be ignored.

type Writer struct {
    // contains filtered or unexported fields
}

func NewStreamingWriter

func NewStreamingWriter(target io.Writer, bufferSize int) Writer

NewStreamingWriter creates a Writer that will buffer a limited amount of its output in memory and dump the output to the specified io.Writer whenever the buffer is full. You should also call Flush at the end of your output to ensure that any remaining buffered output is flushed.

If the Writer returns an error at any point, it enters a failed state and will not try to write any more data to the target.

This function returns the struct by value (Writer, not *Writer). This avoids the overhead of a heap allocation since, in typical usage, the Writer will not escape the scope in which it was declared and can remain on the stack.

Example

Code:

w := NewStreamingWriter(os.Stdout, 10)
obj := w.Object()
obj.Name("property").String("value")
obj.End()
w.Flush()

Output:

{"property":"value"}

func NewWriter

func NewWriter() Writer

NewWriter creates a Writer that will buffer its entire output in memory.

This function returns the struct by value (Writer, not *Writer). This avoids the overhead of a heap allocation since, in typical usage, the Writer will not escape the scope in which it was declared and can remain on the stack.

Example

Code:

w := NewWriter()
obj := w.Object()
obj.Name("property").String("value")
obj.End()
fmt.Println(string(w.Bytes()))

Output:

{"property":"value"}

func (*Writer) AddError

func (w *Writer) AddError(err error)

AddError sets the error state if an error has not already been recorded.

Example

Code:

w := NewWriter()
obj := w.Object()
obj.Name("prop1").Bool(true)
w.AddError(errors.New("sorry, we can't serialize this after all"))
obj.Name("prop2").Bool(true) // no output is generated here because the Writer has already failed
fmt.Println("error is:", w.Error())
fmt.Println("buffer is:", string(w.Bytes()))

Output:

error is: sorry, we can't serialize this after all
buffer is: {"prop1":true

func (*Writer) Array

func (w *Writer) Array() ArrayState

Array begins writing a JSON array to the output. It returns an ArrayState that provides the array formatting; you must call ArrayState.End() when finished.

Example

Code:

w := NewWriter()
arr := w.Array()
arr.Bool(true)
arr.Int(3)
arr.End()
fmt.Println(string(w.Bytes()))

Output:

[true,3]

func (*Writer) Bool

func (w *Writer) Bool(value bool)

Bool writes a JSON boolean value to the output.

Example

Code:

w := NewWriter()
w.Bool(true)
fmt.Println(string(w.Bytes()))

Output:

true

func (*Writer) BoolOrNull

func (w *Writer) BoolOrNull(isDefined bool, value bool)

BoolOrNull is a shortcut for calling Bool(value) if isDefined is true, or else Null().

Example

Code:

w := NewWriter()
w.BoolOrNull(false, true)
fmt.Println(string(w.Bytes()))

Output:

null

func (*Writer) Bytes

func (w *Writer) Bytes() []byte

Bytes returns the full contents of the output buffer.

func (*Writer) Error

func (w *Writer) Error() error

Error returns the first error, if any, that occurred during output generation. If there have been no errors, it returns nil.

As soon as any operation fails at any level, either in the JSON encoding or in writing to an underlying io.Writer, the Writer remembers the error and will generate no further output.

func (*Writer) Float64

func (w *Writer) Float64(value float64)

Float64 writes a JSON numeric value to the output.

Example

Code:

w := NewWriter()
w.Float64(1234.5)
fmt.Println(string(w.Bytes()))

Output:

1234.5

func (*Writer) Float64OrNull

func (w *Writer) Float64OrNull(isDefined bool, value float64)

Float64OrNull is a shortcut for calling Float64(value) if isDefined is true, or else Null().

Example

Code:

w := NewWriter()
w.Float64OrNull(false, 1)
fmt.Println(string(w.Bytes()))

Output:

null

func (*Writer) Flush

func (w *Writer) Flush() error

Flush writes any remaining in-memory output to the underlying io.Writer, if this is a streaming writer created with NewStreamingWriter. It has no effect otherwise.

func (*Writer) Int

func (w *Writer) Int(value int)

Int writes a JSON numeric value to the output.

Example

Code:

w := NewWriter()
w.Int(123)
fmt.Println(string(w.Bytes()))

Output:

123

func (*Writer) IntOrNull

func (w *Writer) IntOrNull(isDefined bool, value int)

IntOrNull is a shortcut for calling Int(value) if isDefined is true, or else Null().

Example

Code:

w := NewWriter()
w.IntOrNull(false, 1)
fmt.Println(string(w.Bytes()))

Output:

null

func (*Writer) Null

func (w *Writer) Null()

Null writes a JSON null value to the output.

Example

Code:

w := NewWriter()
w.Null()
fmt.Println(string(w.Bytes()))

Output:

null

func (*Writer) Object

func (w *Writer) Object() ObjectState

Object begins writing a JSON object to the output. It returns an ObjectState that provides the object formatting; you must call ObjectState.End() when finished.

Example

Code:

w := NewWriter()
obj := w.Object()
obj.Name("boolProperty").Bool(true)
obj.Name("intProperty").Int(3)
obj.End()
fmt.Println(string(w.Bytes()))

Output:

{"boolProperty":true,"intProperty":3}

func (*Writer) Raw

func (w *Writer) Raw(value json.RawMessage)

Raw writes a pre-encoded JSON value to the output as-is. Its format is assumed to be correct; this operation will not fail unless it is not permitted to write a value at this point.

Example

Code:

data := json.RawMessage(`{"value":1}`)
w := NewWriter()
w.Raw(data)

fmt.Println(string(w.Bytes()))

Output:

{"value":1}

func (*Writer) String

func (w *Writer) String(value string)

String writes a JSON string value to the output, adding quotes and performing any necessary escaping.

Example

Code:

w := NewWriter()
w.String(`string says "hello"`)
fmt.Println(string(w.Bytes()))

Output:

"string says \"hello\""

func (*Writer) StringOrNull

func (w *Writer) StringOrNull(isDefined bool, value string)

StringOrNull is a shortcut for calling String(value) if isDefined is true, or else Null().

Example

Code:

w := NewWriter()
w.StringOrNull(false, "no")
fmt.Println(string(w.Bytes()))

Output:

null