oj

package
v0.0.0-...-af45976 Latest Latest
Warning

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

Go to latest
Published: Jan 25, 2023 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package oj contains functions and types to support building simple types where simple types are:

nil
bool
int64
float64
string
time.Time
[]any
map[string]any

Parser

Parse a JSON file or stream. The parser can be used on a single JSON document or a document with multiple JSON elements. An option also exists to allow comments in the JSON.

v, err := oj.ParseString("[true,[false,[null],123],456]")

or for a performance gain on repeated parses:

var p oj.Parser
v, err := p.Parse([]byte("[true,[false,[null],123],456]"))

Validator

Validates a JSON file or stream. It can be used on a single JSON document or a document with multiple JSON elements. An option also exists to allow comments in the JSON.

err := oj.ValidateString("[true,[false,[null],123],456]")

or for a slight performance gain on repeated validations:

var v oj.Validator
err := v.Validate([]byte("[true,[false,[null],123],456]"))

Builder

An example of building simple data is:

var b oj.Builder

b.Object()
b.Value(1, "a")
b.Array("b")
b.Value(2)
b.Pop()
b.Pop()
v := b.Result()

// v: map[string]any{"a": 1, "b": []any{2}}

Writer

The writer function's output data values to JSON. The basic oj.JSON() attempts to build JSON from any data provided skipping types that can not be converted.

s := oj.JSON([]any{1, 2, "abc", true})

Output can also be use with an io.Writer.

var b strings.Builder

err := oj.Write(&b, []any{1, 2, "abc", true})

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// DefaultOptions are the default options for the this package.
	DefaultOptions = ojg.DefaultOptions
	// BrightOptions are the bright color options.
	BrightOptions = ojg.BrightOptions

	// HTMLOptions are the options that can be used to encode as HTML JSON.
	HTMLOptions = ojg.HTMLOptions
)

Functions

func JSON

func JSON(data any, args ...any) string

JSON returns a JSON string for the data provided. The data can be a simple type of nil, bool, int, floats, time.Time, []any, or map[string]any or a Node type, The args, if supplied can be an int as an indent or a *Options.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
)

func main() {
	type Valley struct {
		Val int `json:"value"`
	}
	b := oj.JSON(&Valley{Val: 3})
	fmt.Printf("%s\n", b)
}
Output:

{"val":3}

func Load

func Load(r io.Reader, args ...any) (any, error)

Load a JSON from a io.Reader into a simple type. An error is returned if not valid JSON.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/khaf/ojg/oj"
	"github.com/khaf/ojg/pretty"
)

func main() {
	r := strings.NewReader(`[true,false,[3,2,1],{"a":1,"b":2,"c":3,"d":["x","y","z",[]]}]`)
	val, err := oj.Load(r)
	if err != nil {
		panic(err)
	}
	fmt.Println(pretty.JSON(val, 80.3))

}
Output:

[
  true,
  false,
  [3, 2, 1],
  {"a": 1, "b": 2, "c": 3, "d": ["x", "y", "z", []]}
]

func Marshal

func Marshal(data any, args ...any) (out []byte, err error)

Marshal returns a JSON string for the data provided. The data can be a simple type of nil, bool, int, floats, time.Time, []any, or map[string]any or a gen.Node type, The args, if supplied can be an int as an indent, *ojg.Options, or a *Writer. An error will be returned if the Option.Strict flag is true and a value is encountered that can not be encoded other than by using the %v format of the fmt package.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
)

func main() {
	type Valley struct {
		Val int `json:"value"`
	}
	b, err := oj.Marshal(&Valley{Val: 3})
	fmt.Printf("%v %s\n", err, b)
}
Output:

<nil> {"value":3}

func MustLoad

func MustLoad(r io.Reader, args ...any) (n any)

MustLoad a JSON from a io.Reader into a simple type. Panics on error.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/khaf/ojg/oj"
	"github.com/khaf/ojg/pretty"
)

func main() {
	r := strings.NewReader(`[true,false,[3,2,1],{"a":1,"b":2,"c":3,"d":["x","y","z",[]]}]`)
	val := oj.MustLoad(r)
	fmt.Println(pretty.JSON(val, 80.3))

}
Output:

[
  true,
  false,
  [3, 2, 1],
  {"a": 1, "b": 2, "c": 3, "d": ["x", "y", "z", []]}
]

func MustParse

func MustParse(b []byte, args ...any) (n any)

MustParse JSON into a simple type. Arguments are optional and can be a bool, func(any) bool for callbacks, or a chan any for chan based result delivery. Panics on error

A bool indicates the NoComment parser attribute should be set to the bool value.

A func argument is the callback for the parser if processing multiple JSONs. If no callback function is provided the processing is limited to only one JSON.

A chan argument will be used to deliver parse results.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
	"github.com/khaf/ojg/pretty"
)

func main() {
	val := oj.MustParse([]byte(`[true,false,[3,2,1],{"a":1,"b":2,"c":3,"d":["x","y","z",[]]}]`))
	fmt.Println(pretty.JSON(val, 80.3))

}
Output:

[
  true,
  false,
  [3, 2, 1],
  {"a": 1, "b": 2, "c": 3, "d": ["x", "y", "z", []]}
]

func MustParseString

func MustParseString(s string, args ...any) (n any)

MustParseString is similar to MustParse except it takes a string argument to be parsed instead of a []byte.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
	"github.com/khaf/ojg/pretty"
)

func main() {
	val := oj.MustParseString(`[true,false,[3,2,1],{"a":1,"b":2,"c":3,"d":["x","y","z",[]]}]`)
	fmt.Println(pretty.JSON(val, 80.3))

}
Output:

[
  true,
  false,
  [3, 2, 1],
  {"a": 1, "b": 2, "c": 3, "d": ["x", "y", "z", []]}
]

func Parse

func Parse(b []byte, args ...any) (n any, err error)

Parse JSON into a simple type. Arguments are optional and can be a bool, func(any) bool for callbacks, or a chan any for chan based result delivery.

A bool indicates the NoComment parser attribute should be set to the bool value.

A func argument is the callback for the parser if processing multiple JSONs. If no callback function is provided the processing is limited to only one JSON.

A chan argument will be used to deliver parse results.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
	"github.com/khaf/ojg/pretty"
)

func main() {
	val, err := oj.Parse([]byte(`[true,false,[3,2,1],{"a":1,"b":2,"c":3,"d":["x","y","z",[]]}]`))
	if err != nil {
		panic(err)
	}
	fmt.Println(pretty.JSON(val, 80.3))

}
Output:

[
  true,
  false,
  [3, 2, 1],
  {"a": 1, "b": 2, "c": 3, "d": ["x", "y", "z", []]}
]

func ParseString

func ParseString(s string, args ...any) (n any, err error)

ParseString is similar to Parse except it takes a string argument to be parsed instead of a []byte.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
)

func main() {
	v, err := oj.ParseString(`{"a": 1, "b":[2,3,4]}`)
	if err == nil {
		// Sorted output allows for consistent results.
		fmt.Println(oj.JSON(v, &oj.Options{Sort: true}))
	} else {
		fmt.Println(err.Error())
	}
}
Output:

{"a":1,"b":[2,3,4]}

func Tokenize

func Tokenize(data []byte, handler TokenHandler) error

Tokenize the provided JSON and call the TokenHandler functions for each token in the JSON.

func TokenizeLoad

func TokenizeLoad(r io.Reader, handler TokenHandler) error

TokenizeLoad JSON from a io.Reader and call the TokenHandler functions for each token in the JSON.

func TokenizeString

func TokenizeString(data string, handler TokenHandler) error

TokenizeString the provided JSON and call the handler functions for each token in the JSON.

func Unmarshal

func Unmarshal(data []byte, vp any, recomposer ...*alt.Recomposer) (err error)

Unmarshal parses the provided JSON and stores the result in the value pointed to by vp.

Example (Interface)
package main

import (
	"fmt"

	"github.com/khaf/ojg"
	"github.com/khaf/ojg/alt"
	"github.com/khaf/ojg/oj"
)

// Encode and decode slice of interfaces.

type Animal interface {
	Kind() string
}

type Dog struct {
	Size string
}

func (d *Dog) Kind() string {
	return fmt.Sprintf("%s dog", d.Size)
}

type Cat struct {
	Color string
}

func (c *Cat) Kind() string {
	return fmt.Sprintf("%s cat", c.Color)
}

func main() {
	pets := []Animal{&Dog{Size: "big"}, &Cat{Color: "black"}}

	// Encode and use a create key to identify the encoded type.
	b, err := oj.Marshal(pets, &ojg.Options{CreateKey: "^", Sort: true})
	if err != nil {
		panic(err)
	}
	// Sort the object members in the output for repeatability.
	fmt.Printf("as JSON: %s\n", b)

	// Create a new Recomposer. This can be use over and over again. Register
	// the types with a nil creation function to let reflection do the work
	// since the types are exported.
	var r *alt.Recomposer
	if r, err = alt.NewRecomposer("^", map[any]alt.RecomposeFunc{&Dog{}: nil, &Cat{}: nil}); err != nil {
		panic(err)
	}
	var result any
	if err = oj.Unmarshal(b, &result, r); err != nil {
		panic(err)
	}
	list, _ := result.([]any)
	for _, item := range list {
		animal, _ := item.(Animal)
		fmt.Printf("  %s\n", animal.Kind())
	}
	// Unmarshal with a typed target.
	var animals []Animal
	if err = oj.Unmarshal(b, &animals, r); err != nil {
		panic(err)
	}
	fmt.Println("Unmarshal into a target struct")
	for _, animal := range animals {
		fmt.Printf("  %T - %s\n", animal, animal.Kind())
	}

}
Output:

as JSON: [{"^":"Dog","size":"big"},{"^":"Cat","color":"black"}]
  big dog
  black cat
Unmarshal into a target struct
  *oj_test.Dog - big dog
  *oj_test.Cat - black cat

func Validate

func Validate(b []byte) error

Validate a JSON string. An error is returned if not valid JSON.

func ValidateReader

func ValidateReader(r io.Reader) error

ValidateReader a JSON stream. An error is returned if not valid JSON.

func ValidateString

func ValidateString(s string) error

ValidateString a JSON string. An error is returned if not valid JSON.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
)

func main() {
	err := oj.ValidateString(`{"a": 1, "b":[2,3,4]}`)
	fmt.Println(oj.JSON(err))
}
Output:

null

func Write

func Write(w io.Writer, data any, args ...any) (err error)

Write a JSON string for the data provided. The data can be a simple type of nil, bool, int, floats, time.Time, []any, or map[string]any or a Node type, The args, if supplied can be an int as an indent or a *Options.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/khaf/ojg"
	"github.com/khaf/ojg/oj"
)

func main() {
	var b strings.Builder
	data := []any{
		map[string]any{
			"x": 1,
			"y": 2,
		},
	}
	if err := oj.Write(&b, data, &ojg.Options{Sort: true}); err != nil {
		panic(err)
	}
	fmt.Println(b.String())

}
Output:

[{"x":1,"y":2}]

Types

type Builder

type Builder = alt.Builder

Builder is an aliase for alt.Builder.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
)

func main() {
	var b oj.Builder

	_ = b.Object()
	_ = b.Array("a")
	_ = b.Value(true)
	_ = b.Object()
	_ = b.Value(123, "x")
	b.Pop()
	_ = b.Value(nil)
	b.PopAll()
	v := b.Result()
	fmt.Println(oj.JSON(v))
}
Output:

{"a":[true,{"x":123},null]}

type Options

type Options = ojg.Options

Options is an alias for ojg.Options

type ParseError

type ParseError struct {
	Message string
	Line    int
	Column  int
}

ParseError represents a parse error.

func (*ParseError) Error

func (err *ParseError) Error() string

Error returns a string representation of the error.

type Parser

type Parser struct {

	// Reuse maps. Previously returned maps will no longer be valid or rather
	// could be modified during parsing.
	Reuse bool
	// contains filtered or unexported fields
}

Parser is a reusable JSON parser. It can be reused for multiple parsings which allows buffer reuse for a performance advantage.

func (*Parser) Parse

func (p *Parser) Parse(buf []byte, args ...any) (any, error)

Parse a JSON string in to simple types. An error is returned if not valid JSON.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
)

func main() {
	// The parser can be reused for better performance by reusing buffers.
	var p oj.Parser
	v, err := p.Parse([]byte(`{"a": 1, "b":[2,3,4]}`))
	if err == nil {
		// Sorted output allows for consistent results.
		fmt.Println(oj.JSON(v, &oj.Options{Sort: true}))
	} else {
		fmt.Println(err.Error())
	}
}
Output:

{"a":1,"b":[2,3,4]}
Example (Callback)
package main

import (
	"fmt"

	"github.com/khaf/ojg/oj"
)

func main() {
	var results []byte
	cb := func(n any) bool {
		if 0 < len(results) {
			results = append(results, ' ')
		}
		results = append(results, oj.JSON(n)...)
		return false
	}
	var p oj.Parser
	_, _ = p.Parse([]byte("[1,2][3,4][5,6]"), cb)
	fmt.Println(string(results))
}
Output:

[1,2] [3,4] [5,6]

func (*Parser) ParseReader

func (p *Parser) ParseReader(r io.Reader, args ...any) (data any, err error)

ParseReader reads JSON from an io.Reader. An error is returned if not valid JSON.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/khaf/ojg/oj"
)

func main() {
	// The parser can be reused for better performance by reusing buffers.
	var p oj.Parser
	v, err := p.ParseReader(strings.NewReader(`{"a": 1, "b":[2,3,4]}`))
	if err == nil {
		// Sorted output allows for consistent results.
		fmt.Println(oj.JSON(v, &oj.Options{Sort: true}))
	} else {
		fmt.Println(err.Error())
	}
}
Output:

{"a":1,"b":[2,3,4]}

func (*Parser) Unmarshal

func (p *Parser) Unmarshal(data []byte, vp any, recomposer ...alt.Recomposer) (err error)

Unmarshal parses the provided JSON and stores the result in the value pointed to by vp.

type SimpleParser

type SimpleParser interface {
	// Parse a string in to simple types. An error is returned if not valid.
	Parse(buf []byte, args ...any) (data any, err error)

	// ParseReader an io.Reader. An error is returned if not valid.
	ParseReader(r io.Reader, args ...any) (node any, err error)
}

SimpleParser is the interface shared by the package parsers.

type TokenHandler

type TokenHandler interface {
	// Null is called when a JSON null is encountered.
	Null()

	// Bool is called when a JSON true or false is encountered.
	Bool(bool)

	// Int is called when a JSON integer is encountered.
	Int(int64)

	// Float is called when a JSON decimal is encountered that fits into a
	// float64.
	Float(float64)

	// Number is called when a JSON number is encountered that does not fit
	// into an int64 or float64.
	Number(string)

	// String is called when a JSON string is encountered.
	String(string)

	// ObjectStart is called when a JSON object start '{' is encountered.
	ObjectStart()

	// ObjectEnd is called when a JSON object end '}' is encountered.
	ObjectEnd()

	// Key is called when a JSON object key is encountered.
	Key(string)

	// ArrayStart is called when a JSON array start '[' is encountered.
	ArrayStart()

	// ArrayEnd is called when a JSON array end ']' is encountered.
	ArrayEnd()
}

TokenHandler describes an interface for handling tokens when using the Tokenizer for parsing JSON or SEN documents.

type Tokenizer

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

Tokenizer is used to tokenize a JSON document.

func (*Tokenizer) Load

func (t *Tokenizer) Load(r io.Reader, handler TokenHandler) (err error)

Load aand parse the JSON and call the handler functions for each token in the JSON.

func (*Tokenizer) Parse

func (t *Tokenizer) Parse(buf []byte, handler TokenHandler) (err error)

Parse the JSON and call the handler functions for each token in the JSON.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/khaf/ojg/oj"
)

type Toker struct {
	buf []byte
}

func (h *Toker) Null() {
	h.buf = append(h.buf, "null "...)
}

func (h *Toker) Bool(v bool) {
	h.buf = append(h.buf, fmt.Sprintf("%t ", v)...)
}

func (h *Toker) Int(v int64) {
	h.buf = append(h.buf, fmt.Sprintf("%d ", v)...)
}

func (h *Toker) Float(v float64) {
	h.buf = append(h.buf, fmt.Sprintf("%g ", v)...)
}

func (h *Toker) Number(v string) {
	h.buf = append(h.buf, fmt.Sprintf("%s ", v)...)
}

func (h *Toker) String(v string) {
	h.buf = append(h.buf, fmt.Sprintf("%s ", v)...)
}

func (h *Toker) ObjectStart() {
	h.buf = append(h.buf, '{')
	h.buf = append(h.buf, ' ')
}

func (h *Toker) ObjectEnd() {
	h.buf = append(h.buf, '}')
	h.buf = append(h.buf, ' ')
}

func (h *Toker) Key(v string) {
	h.buf = append(h.buf, fmt.Sprintf("%s: ", v)...)
}

func (h *Toker) ArrayStart() {
	h.buf = append(h.buf, '[')
	h.buf = append(h.buf, ' ')
}

func (h *Toker) ArrayEnd() {
	h.buf = append(h.buf, ']')
	h.buf = append(h.buf, ' ')
}

func main() {
	toker := oj.Tokenizer{}
	h := Toker{}
	src := `[true,null,123,12.3]{"x":12345678901234567890}`
	if err := toker.Parse([]byte(src), &h); err != nil {
		panic(err)
	}
	fmt.Println(string(bytes.TrimSpace(h.buf)))

}
Output:

[ true null 123 12.3 ] { x: 12345678901234567890 }

type Validator

type Validator struct {

	// OnlyOne returns an error if more than one JSON is in the string or
	// stream.
	OnlyOne bool
	// contains filtered or unexported fields
}

Validator is a reusable JSON validator. It can be reused for multiple validations or parsings which allows buffer reuse for a performance advantage.

func (*Validator) Validate

func (p *Validator) Validate(buf []byte) (err error)

Validate a JSON encoded byte slice.

func (*Validator) ValidateReader

func (p *Validator) ValidateReader(r io.Reader) error

ValidateReader a JSON stream. An error is returned if not valid JSON.

type Writer

type Writer struct {
	ojg.Options
	// contains filtered or unexported fields
}

Writer is a JSON writer that includes a reused buffer for reduced allocations for repeated encoding calls.

func (*Writer) JSON

func (wr *Writer) JSON(data any) string

JSON writes data, JSON encoded. On error, an empty string is returned.

Example
package main

import (
	"fmt"

	"github.com/khaf/ojg"
	"github.com/khaf/ojg/oj"
)

func main() {
	data := []any{
		map[string]any{
			"x": 1,
			"y": 2,
		},
	}
	wr := oj.Writer{Options: ojg.Options{Sort: true}}
	j := wr.JSON(data)
	fmt.Println(j)

}
Output:

[{"x":1,"y":2}]

func (*Writer) MustJSON

func (wr *Writer) MustJSON(data any) []byte

MustJSON writes data, JSON encoded as a []byte and not a string like the JSON() function. On error a panic is called with the error. The returned buffer is the Writer buffer and is reused on the next call to write. If returned value is to be preserved past a second invocation then the buffer should be copied.

func (*Writer) MustWrite

func (wr *Writer) MustWrite(w io.Writer, data any)

MustWrite a JSON string for the data provided. If an error occurs panic is called with the error.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/khaf/ojg"
	"github.com/khaf/ojg/oj"
)

func main() {
	var b strings.Builder
	data := []any{
		map[string]any{
			"x": 1,
			"y": 2,
		},
	}
	wr := oj.Writer{Options: ojg.Options{Sort: true}}
	wr.MustWrite(&b, data)
	fmt.Println(b.String())

}
Output:

[{"x":1,"y":2}]

func (*Writer) Write

func (wr *Writer) Write(w io.Writer, data any) (err error)

Write a JSON string for the data provided.

type ZeroHandler

type ZeroHandler struct {
}

ZeroHandler is a TokenHandler whose functions do nothing. It is used as an embedded member for TokenHandlers that don't care about all of the TokenHandler functions.

func (*ZeroHandler) ArrayEnd

func (z *ZeroHandler) ArrayEnd()

ArrayEnd is called when a JSON array end ']' is encountered.

func (*ZeroHandler) ArrayStart

func (z *ZeroHandler) ArrayStart()

ArrayStart is called when a JSON array start '[' is encountered.

func (*ZeroHandler) Bool

func (z *ZeroHandler) Bool(bool)

Bool is called when a JSON true or false is encountered.

func (*ZeroHandler) Float

func (z *ZeroHandler) Float(float64)

Float is called when a JSON decimal is encountered that fits into a float64.

func (*ZeroHandler) Int

func (z *ZeroHandler) Int(int64)

Int is called when a JSON integer is encountered.

func (*ZeroHandler) Key

func (z *ZeroHandler) Key(string)

Key is called when a JSON object key is encountered.

func (*ZeroHandler) Null

func (z *ZeroHandler) Null()

Null is called when a JSON null is encountered.

func (*ZeroHandler) Number

func (z *ZeroHandler) Number(string)

Number is called when a JSON number is encountered that does not fit into an int64 or float64.

func (*ZeroHandler) ObjectEnd

func (z *ZeroHandler) ObjectEnd()

ObjectEnd is called when a JSON object end '}' is encountered.

func (*ZeroHandler) ObjectStart

func (z *ZeroHandler) ObjectStart()

ObjectStart is called when a JSON object start '{' is encountered.

func (*ZeroHandler) String

func (z *ZeroHandler) String(string)

String is called when a JSON string is encountered.

Jump to

Keyboard shortcuts

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