jwriter

package
v3.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 18, 2024 License: Apache-2.0 Imports: 5 Imported by: 16

Documentation

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
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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

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.

Types

type ArrayState

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

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.

func (*ArrayState) Array

func (arr *ArrayState) Array() ArrayState

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

Example
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
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
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
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
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
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
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
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

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

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.

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
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
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

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)
}

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

type Writer

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

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.

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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
w := NewWriter()
w.StringOrNull(false, "no")
fmt.Println(string(w.Bytes()))
Output:

null

Jump to

Keyboard shortcuts

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