gabs

package module
v2.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2020 License: MIT Imports: 8 Imported by: 399

README

Gabs

pkg.go for Jeffail/gabs

Gabs is a small utility for dealing with dynamic or unknown JSON structures in Go. It's pretty much just a helpful wrapper for navigating hierarchies of map[string]interface{} objects provided by the encoding/json package. It does nothing spectacular apart from being fabulous.

If you're migrating from version 1 check out migration.md for guidance.

Use

Import

Using modules:

import (
	"github.com/Jeffail/gabs/v2"
)

Without modules:

import (
	"github.com/Jeffail/gabs"
)

Parsing and searching JSON

jsonParsed, err := gabs.ParseJSON([]byte(`{
	"outter":{
		"inner":{
			"value1":10,
			"value2":22
		},
		"alsoInner":{
			"value1":20,
			"array1":[
				30, 40
			]
		}
	}
}`))
if err != nil {
	panic(err)
}

var value float64
var ok bool

value, ok = jsonParsed.Path("outter.inner.value1").Data().(float64)
// value == 10.0, ok == true

value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64)
// value == 10.0, ok == true

value, ok = jsonParsed.Search("outter", "alsoInner", "array1", "1").Data().(float64)
// value == 40.0, ok == true

gObj, err := jsonParsed.JSONPointer("/outter/alsoInner/array1/1")
if err != nil {
	panic(err)
}
value, ok = gObj.Data().(float64)
// value == 40.0, ok == true

value, ok = jsonParsed.Path("does.not.exist").Data().(float64)
// value == 0.0, ok == false

exists := jsonParsed.Exists("outter", "inner", "value1")
// exists == true

exists = jsonParsed.ExistsP("does.not.exist")
// exists == false

Iterating objects

jsonParsed, err := gabs.ParseJSON([]byte(`{"object":{"first":1,"second":2,"third":3}}`))
if err != nil {
	panic(err)
}

// S is shorthand for Search
for key, child := range jsonParsed.S("object").ChildrenMap() {
	fmt.Printf("key: %v, value: %v\n", key, child.Data().(string))
}

Iterating arrays

jsonParsed, err := gabs.ParseJSON([]byte(`{"array":["first","second","third"]}`))
if err != nil {
	panic(err)
}

for _, child := range jsonParsed.S("array").Children() {
	fmt.Println(child.Data().(string))
}

Will print:

first
second
third

Children() will return all children of an array in order. This also works on objects, however, the children will be returned in a random order.

Searching through arrays

If your structure contains arrays you must target an index in your search.

jsonParsed, err := gabs.ParseJSON([]byte(`{"array":[{"value":1},{"value":2},{"value":3}]}`))
if err != nil {
	panic(err)
}
fmt.Println(jsonParsed.Path("array.1.value").String())

Will print 2.

Generating JSON

jsonObj := gabs.New()
// or gabs.Wrap(jsonObject) to work on an existing map[string]interface{}

jsonObj.Set(10, "outter", "inner", "value")
jsonObj.SetP(20, "outter.inner.value2")
jsonObj.Set(30, "outter", "inner2", "value3")

fmt.Println(jsonObj.String())

Will print:

{"outter":{"inner":{"value":10,"value2":20},"inner2":{"value3":30}}}

To pretty-print:

fmt.Println(jsonObj.StringIndent("", "  "))

Will print:

{
  "outter": {
    "inner": {
      "value": 10,
      "value2": 20
    },
    "inner2": {
      "value3": 30
    }
  }
}

Generating Arrays

jsonObj := gabs.New()

jsonObj.Array("foo", "array")
// Or .ArrayP("foo.array")

jsonObj.ArrayAppend(10, "foo", "array")
jsonObj.ArrayAppend(20, "foo", "array")
jsonObj.ArrayAppend(30, "foo", "array")

fmt.Println(jsonObj.String())

Will print:

{"foo":{"array":[10,20,30]}}

Working with arrays by index:

jsonObj := gabs.New()

// Create an array with the length of 3
jsonObj.ArrayOfSize(3, "foo")

jsonObj.S("foo").SetIndex("test1", 0)
jsonObj.S("foo").SetIndex("test2", 1)

// Create an embedded array with the length of 3
jsonObj.S("foo").ArrayOfSizeI(3, 2)

jsonObj.S("foo").Index(2).SetIndex(1, 0)
jsonObj.S("foo").Index(2).SetIndex(2, 1)
jsonObj.S("foo").Index(2).SetIndex(3, 2)

fmt.Println(jsonObj.String())

Will print:

{"foo":["test1","test2",[1,2,3]]}

Converting back to JSON

This is the easiest part:

jsonParsedObj, _ := gabs.ParseJSON([]byte(`{
	"outter":{
		"values":{
			"first":10,
			"second":11
		}
	},
	"outter2":"hello world"
}`))

jsonOutput := jsonParsedObj.String()
// Becomes `{"outter":{"values":{"first":10,"second":11}},"outter2":"hello world"}`

And to serialize a specific segment is as simple as:

jsonParsedObj := gabs.ParseJSON([]byte(`{
	"outter":{
		"values":{
			"first":10,
			"second":11
		}
	},
	"outter2":"hello world"
}`))

jsonOutput := jsonParsedObj.Search("outter").String()
// Becomes `{"values":{"first":10,"second":11}}`

Merge two containers

You can merge a JSON structure into an existing one, where collisions will be converted into a JSON array.

jsonParsed1, _ := ParseJSON([]byte(`{"outter":{"value1":"one"}}`))
jsonParsed2, _ := ParseJSON([]byte(`{"outter":{"inner":{"value3":"three"}},"outter2":{"value2":"two"}}`))

jsonParsed1.Merge(jsonParsed2)
// Becomes `{"outter":{"inner":{"value3":"three"},"value1":"one"},"outter2":{"value2":"two"}}`

Arrays are merged:

jsonParsed1, _ := ParseJSON([]byte(`{"array":["one"]}`))
jsonParsed2, _ := ParseJSON([]byte(`{"array":["two"]}`))

jsonParsed1.Merge(jsonParsed2)
// Becomes `{"array":["one", "two"]}`

Parsing Numbers

Gabs uses the json package under the bonnet, which by default will parse all number values into float64. If you need to parse Int values then you should use a json.Decoder:

sample := []byte(`{"test":{"int":10,"float":6.66}}`)
dec := json.NewDecoder(bytes.NewReader(sample))
dec.UseNumber()

val, err := gabs.ParseJSONDecoder(dec)
if err != nil {
    t.Errorf("Failed to parse: %v", err)
    return
}

intValue, err := val.Path("test.int").Data().(json.Number).Int64()

Documentation

Overview

Package gabs implements a wrapper around creating and parsing unknown or dynamic map structures resulting from JSON parsing.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrOutOfBounds indicates an index was out of bounds.
	ErrOutOfBounds = errors.New("out of bounds")

	// ErrNotObjOrArray is returned when a target is not an object or array type
	// but needs to be for the intended operation.
	ErrNotObjOrArray = errors.New("not an object or array")

	// ErrNotObj is returned when a target is not an object but needs to be for
	// the intended operation.
	ErrNotObj = errors.New("not an object")

	// ErrInvalidQuery is returned when a seach query was not valid.
	ErrInvalidQuery = errors.New("invalid search query")

	// ErrNotArray is returned when a target is not an array but needs to be for
	// the intended operation.
	ErrNotArray = errors.New("not an array")

	// ErrPathCollision is returned when creating a path failed because an
	// element collided with an existing value.
	ErrPathCollision = errors.New("encountered value collision whilst building path")

	// ErrInvalidInputObj is returned when the input value was not a
	// map[string]interface{}.
	ErrInvalidInputObj = errors.New("invalid input object")

	// ErrInvalidInputText is returned when the input data could not be parsed.
	ErrInvalidInputText = errors.New("input text could not be parsed")

	// ErrNotFound is returned when a query leaf is not found.
	ErrNotFound = errors.New("field not found")

	// ErrInvalidPath is returned when the filepath was not valid.
	ErrInvalidPath = errors.New("invalid file path")

	// ErrInvalidBuffer is returned when the input buffer contained an invalid
	// JSON string.
	ErrInvalidBuffer = errors.New("input buffer contained invalid JSON")
)

Functions

func DotPathToSlice added in v2.2.0

func DotPathToSlice(path string) []string

DotPathToSlice returns a slice of path segments parsed out of a dot path.

Because the characters '~' (%x7E) and '.' (%x2E) have special meanings in gabs paths, '~' needs to be encoded as '~0' and '.' needs to be encoded as '~1' when these characters appear in a reference key.

func JSONPointerToSlice added in v2.2.0

func JSONPointerToSlice(path string) ([]string, error)

JSONPointerToSlice parses a JSON pointer path (https://tools.ietf.org/html/rfc6901) and returns the path segments as a slice.

Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in gabs paths, '~' needs to be encoded as '~0' and '/' needs to be encoded as '~1' when these characters appear in a reference key.

Types

type Container

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

Container references a specific element within a wrapped structure.

func New

func New() *Container

New creates a new gabs JSON object.

func ParseJSON

func ParseJSON(sample []byte) (*Container, error)

ParseJSON unmarshals a JSON byte slice into a *Container.

func ParseJSONBuffer

func ParseJSONBuffer(buffer io.Reader) (*Container, error)

ParseJSONBuffer reads a buffer and unmarshals the contents into a *Container.

func ParseJSONDecoder

func ParseJSONDecoder(decoder *json.Decoder) (*Container, error)

ParseJSONDecoder applies a json.Decoder to a *Container.

func ParseJSONFile

func ParseJSONFile(path string) (*Container, error)

ParseJSONFile reads a file and unmarshals the contents into a *Container.

func Wrap

func Wrap(root interface{}) *Container

Wrap an already unmarshalled JSON object (or a new map[string]interface{}) into a *Container.

func (*Container) Array

func (g *Container) Array(hierarchy ...string) (*Container, error)

Array creates a new JSON array at a path. Returns an error if the path contains a collision with a non object type.

func (*Container) ArrayAppend

func (g *Container) ArrayAppend(value interface{}, hierarchy ...string) error

ArrayAppend attempts to append a value onto a JSON array at a path. If the target is not a JSON array then it will be converted into one, with its original contents set to the first element of the array.

func (*Container) ArrayAppendP

func (g *Container) ArrayAppendP(value interface{}, path string) error

ArrayAppendP attempts to append a value onto a JSON array at a path using dot notation. If the target is not a JSON array then it will be converted into one, with its original contents set to the first element of the array.

func (*Container) ArrayConcat added in v2.4.0

func (g *Container) ArrayConcat(value interface{}, hierarchy ...string) error

ArrayConcat attempts to append a value onto a JSON array at a path. If the target is not a JSON array then it will be converted into one, with its original contents set to the first element of the array.

ArrayConcat differs from ArrayAppend in that it will expand a value type []interface{} during the append operation, resulting in concatenation of each element, rather than append as a single element of []interface{}.

func (*Container) ArrayConcatP added in v2.4.0

func (g *Container) ArrayConcatP(value interface{}, path string) error

ArrayConcatP attempts to append a value onto a JSON array at a path using dot notation. If the target is not a JSON array then it will be converted into one, with its original contents set to the first element of the array.

ArrayConcatP differs from ArrayAppendP in that it will expand a value type []interface{} during the append operation, resulting in concatenation of each element, rather than append as a single element of []interface{}.

func (*Container) ArrayCount

func (g *Container) ArrayCount(hierarchy ...string) (int, error)

ArrayCount counts the number of elements in a JSON array at a path.

func (*Container) ArrayCountP

func (g *Container) ArrayCountP(path string) (int, error)

ArrayCountP counts the number of elements in a JSON array at a path using dot notation.

func (*Container) ArrayElement

func (g *Container) ArrayElement(index int, hierarchy ...string) (*Container, error)

ArrayElement attempts to access an element by an index from a JSON array at a path.

func (*Container) ArrayElementP

func (g *Container) ArrayElementP(index int, path string) (*Container, error)

ArrayElementP attempts to access an element by an index from a JSON array at a path using dot notation.

func (*Container) ArrayI

func (g *Container) ArrayI(index int) (*Container, error)

ArrayI creates a new JSON array within an array at an index. Returns an error if the element is not an array or the index is out of bounds.

func (*Container) ArrayOfSize

func (g *Container) ArrayOfSize(size int, hierarchy ...string) (*Container, error)

ArrayOfSize creates a new JSON array of a particular size at a path. Returns an error if the path contains a collision with a non object type.

func (*Container) ArrayOfSizeI

func (g *Container) ArrayOfSizeI(size, index int) (*Container, error)

ArrayOfSizeI create a new JSON array of a particular size within an array at an index. Returns an error if the element is not an array or the index is out of bounds.

func (*Container) ArrayOfSizeP

func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error)

ArrayOfSizeP creates a new JSON array of a particular size at a path using dot notation. Returns an error if the path contains a collision with a non object type.

func (*Container) ArrayP

func (g *Container) ArrayP(path string) (*Container, error)

ArrayP creates a new JSON array at a path using dot notation. Returns an error if the path contains a collision with a non object type.

func (*Container) ArrayRemove

func (g *Container) ArrayRemove(index int, hierarchy ...string) error

ArrayRemove attempts to remove an element identified by an index from a JSON array at a path.

func (*Container) ArrayRemoveP

func (g *Container) ArrayRemoveP(index int, path string) error

ArrayRemoveP attempts to remove an element identified by an index from a JSON array at a path using dot notation.

func (*Container) Bytes

func (g *Container) Bytes() []byte

Bytes marshals an element to a JSON []byte blob.

func (*Container) BytesIndent

func (g *Container) BytesIndent(prefix string, indent string) []byte

BytesIndent marshals an element to a JSON []byte blob formatted with a prefix and indent string.

func (*Container) Children

func (g *Container) Children() []*Container

Children returns a slice of all children of an array element. This also works for objects, however, the children returned for an object will be in a random order and you lose the names of the returned objects this way. If the underlying container value isn't an array or map nil is returned.

func (*Container) ChildrenMap

func (g *Container) ChildrenMap() map[string]*Container

ChildrenMap returns a map of all the children of an object element. IF the underlying value isn't a object then an empty map is returned.

func (*Container) Data

func (g *Container) Data() interface{}

Data returns the underlying value of the target element in the wrapped structure.

func (*Container) Delete

func (g *Container) Delete(hierarchy ...string) error

Delete an element at a path, an error is returned if the element does not exist or is not an object. In order to remove an array element please use ArrayRemove.

func (*Container) DeleteP

func (g *Container) DeleteP(path string) error

DeleteP deletes an element at a path using dot notation, an error is returned if the element does not exist.

func (*Container) EncodeJSON

func (g *Container) EncodeJSON(encodeOpts ...EncodeOpt) []byte

EncodeJSON marshals an element to a JSON formatted []byte using a variant list of modifier functions for the encoder being used. Functions for modifying the output are prefixed with EncodeOpt, e.g. EncodeOptHTMLEscape.

func (*Container) Exists

func (g *Container) Exists(hierarchy ...string) bool

Exists checks whether a field exists within the hierarchy.

func (*Container) ExistsP

func (g *Container) ExistsP(path string) bool

ExistsP checks whether a dot notation path exists.

func (*Container) Flatten added in v2.5.0

func (g *Container) Flatten() (map[string]interface{}, error)

Flatten a JSON array or object into an object of key/value pairs for each field, where the key is the full path of the structured field in dot path notation matching the spec for the method Path.

E.g. the structure `{"foo":[{"bar":"1"},{"bar":"2"}]}` would flatten into the object: `{"foo.0.bar":"1","foo.1.bar":"2"}`. `{"foo": [{"bar":[]},{"bar":{}}]}` would flatten into the object `{}`

Returns an error if the target is not a JSON object or array.

func (*Container) FlattenIncludeEmpty added in v2.6.0

func (g *Container) FlattenIncludeEmpty() (map[string]interface{}, error)

FlattenIncludeEmpty a JSON array or object into an object of key/value pairs for each field, just as Flatten, but includes empty arrays and objects, where the key is the full path of the structured field in dot path notation matching the spec for the method Path.

E.g. the structure `{"foo": [{"bar":[]},{"bar":{}}]}` would flatten into the object: `{"foo.0.bar":[],"foo.1.bar":{}}`.

Returns an error if the target is not a JSON object or array.

func (*Container) Index

func (g *Container) Index(index int) *Container

Index attempts to find and return an element within a JSON array by an index.

func (*Container) JSONPointer

func (g *Container) JSONPointer(path string) (*Container, error)

JSONPointer parses a JSON pointer path (https://tools.ietf.org/html/rfc6901) and either returns a *gabs.Container containing the result or an error if the referenced item could not be found.

Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in gabs paths, '~' needs to be encoded as '~0' and '/' needs to be encoded as '~1' when these characters appear in a reference key.

func (*Container) MarshalJSON added in v2.3.0

func (g *Container) MarshalJSON() ([]byte, error)

MarshalJSON returns the JSON encoding of this container. This allows structs which contain Container instances to be marshaled using json.Marshal().

func (*Container) Merge

func (g *Container) Merge(source *Container) error

Merge a source object into an existing destination object. When a collision is found within the merged structures (both a source and destination object contain the same non-object keys) the result will be an array containing both values, where values that are already arrays will be expanded into the resulting array.

It is possible to merge structures will different collision behaviours with MergeFn.

func (*Container) MergeFn

func (g *Container) MergeFn(source *Container, collisionFn func(destination, source interface{}) interface{}) error

MergeFn merges two objects using a provided function to resolve collisions.

The collision function receives two interface{} arguments, destination (the original object) and source (the object being merged into the destination). Which ever value is returned becomes the new value in the destination object at the location of the collision.

func (*Container) Object

func (g *Container) Object(hierarchy ...string) (*Container, error)

Object creates a new JSON object at a target path. Returns an error if the path contains a collision with a non object type.

func (*Container) ObjectI

func (g *Container) ObjectI(index int) (*Container, error)

ObjectI creates a new JSON object at an array index. Returns an error if the object is not an array or the index is out of bounds.

func (*Container) ObjectP

func (g *Container) ObjectP(path string) (*Container, error)

ObjectP creates a new JSON object at a target path using dot notation. Returns an error if the path contains a collision with a non object type.

func (*Container) Path

func (g *Container) Path(path string) *Container

Path searches the wrapped structure following a path in dot notation, segments of this path are searched according to the same rules as Search.

Because the characters '~' (%x7E) and '.' (%x2E) have special meanings in gabs paths, '~' needs to be encoded as '~0' and '.' needs to be encoded as '~1' when these characters appear in a reference key.

func (*Container) S

func (g *Container) S(hierarchy ...string) *Container

S is a shorthand alias for Search.

func (*Container) Search

func (g *Container) Search(hierarchy ...string) *Container

Search attempts to find and return an object within the wrapped structure by following a provided hierarchy of field names to locate the target.

If the search encounters an array then the next hierarchy field name must be either a an integer which is interpreted as the index of the target, or the character '*', in which case all elements are searched with the remaining search hierarchy and the results returned within an array.

func (*Container) Set

func (g *Container) Set(value interface{}, hierarchy ...string) (*Container, error)

Set attempts to set the value of a field located by a hierarchy of field names. If the search encounters an array then the next hierarchy field name is interpreted as an integer index of an existing element, or the character '-', which indicates a new element appended to the end of the array.

Any parts of the hierarchy that do not exist will be constructed as objects. This includes parts that could be interpreted as array indexes.

Returns a container of the new value or an error.

func (*Container) SetIndex

func (g *Container) SetIndex(value interface{}, index int) (*Container, error)

SetIndex attempts to set a value of an array element based on an index.

func (*Container) SetJSONPointer

func (g *Container) SetJSONPointer(value interface{}, path string) (*Container, error)

SetJSONPointer parses a JSON pointer path (https://tools.ietf.org/html/rfc6901) and sets the leaf to a value. Returns an error if the pointer could not be resolved due to missing fields.

func (*Container) SetP

func (g *Container) SetP(value interface{}, path string) (*Container, error)

SetP sets the value of a field at a path using dot notation, any parts of the path that do not exist will be constructed, and if a collision occurs with a non object type whilst iterating the path an error is returned.

func (*Container) String

func (g *Container) String() string

String marshals an element to a JSON formatted string.

func (*Container) StringIndent

func (g *Container) StringIndent(prefix string, indent string) string

StringIndent marshals an element to a JSON string formatted with a prefix and indent string.

type EncodeOpt

type EncodeOpt func(e *json.Encoder)

EncodeOpt is a functional option for the EncodeJSON method.

func EncodeOptHTMLEscape

func EncodeOptHTMLEscape(doEscape bool) EncodeOpt

EncodeOptHTMLEscape sets the encoder to escape the JSON for html.

func EncodeOptIndent

func EncodeOptIndent(prefix string, indent string) EncodeOpt

EncodeOptIndent sets the encoder to indent the JSON output.

Jump to

Keyboard shortcuts

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