jsonreflect

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Jan 7, 2021 License: MIT Imports: 15 Imported by: 0

README

jsonreflect

GoDoc

Package provides reflection features for JSON values.

Goal

This package provides reflection features to JSON, which allows working with JSON structure in reflect package manner.

Package might be useful in cases when unmarshal logic depends on source data structure like:

  • Object field type may vary (field can be array or object).
  • Object may contain fields stored in separate properties with same prefix, instead of array.
  • Object structure is polymorphic.

And for other cases of bad JSON structure design.

Examples

Processing unknown values

For instance, there is an object which only have a small subset of known fields and there is a need to separate known values from orphans for future processing.

Example below shows how jsonreflect allows collecting all unknown fields on value unmarshal.

package main

import (
    "fmt"
	
    "github.com/x1unix/jsonreflect"
)

type GenericResponse struct {
    // Known fields
    Status int      `json:"status"`
    Payload []byte  `json:"payload"`
    
    // Container for unknown fields.
    // Also, *json.Value can be used to get values as JSON object.
    Orphan map[string]interface{}  `json:"..."`
}

func unmarshalGenericResponse(data []byte) error {
    rsp := new(GenericResponse)
    if err := jsonreflect.Unmarshal(data, rsp); err != nil {
        return err
    }
    
    // Process orphan fields
    fmt.Println(rsp.Orphan)
    return nil
}

Corrupted object

For instance, we have an JSON response from some service with specified structure, but sometimes service returns response with different structure when internal error occurs.

Normal response

{
  "STATUS": [
    {
      "STATUS": "S",
      "When": 1609265946,
      "Code": 9,
      "Msg": "3 ASC(s)"
    }
  ],
  "DATA": ["some actual data..."]
}

Abnornal response:

{
  "STATUS": "E",
  "When": 1609267826,
  "Code": 14,
  "Msg": "invalid cmd",
  "Description": "cgminer v1.3"
}

In normal cases, STATUS is an array but sometimes it might be a regular object. Let's mitigate this issue.

Example

package main

import (
	"fmt"
	
	"github.com/x1unix/jsonreflect"
)

type Status struct {
	// status struct from json above
}

// checkStatus function checks if one of statuses contains error
func checkStatus(statuses []Status) error

// checkResponseError checks if response has an error
func checkResponseError(resp []byte) error {
    // Check if response has error
    value, err := jsonreflect.ValueOf(resp)
    if err != nil {
        // Invalid json
        return err
    }
    
    // cast response to object
    obj, err := jsonreflect.ToObject(value)
    if err != nil {
    	// handle invalid response
    	return fmt.Errorf("unexpected response: %v", value.Interface())
    }
    
    statusVal := obj.Items["STATUS"]
    
    // wrap status with array
    if jsonreflect.TypeOf(statusVal) != jsonreflect.TypeArray {
    	statusVal = jsonreflect.NewArray(statusVal)
    }
    
    // unmarshal value to struct and do our stuff
    var statuses []Status
    if err = jsonreflect.UnmarshalValue(statusVal, &statuses); err != nil {
    	return err
    }
    
    return checkStatus(statuses)
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotStringable means that value cannot be converted to string representation.
	ErrNotStringable = errors.New("value not stringable")
)

Functions

func MarshalValue

func MarshalValue(v Value, opts *MarshalOptions) ([]byte, error)

MarshalValue returns the JSON encoding of passed jsonreflect.Value

Accepts optional argument which allows to specify indent.

func Unmarshal

func Unmarshal(src []byte, dst interface{}, opts ...UnmarshalOption) error

Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. Accepts additional options to customise unmarshal process.

Method supports the same tag and behavior as standard json.Unmarshal method.

See UnmarshalValue documentation for information about extended behavior.

func UnmarshalValue

func UnmarshalValue(v Value, dst interface{}, opts ...UnmarshalOption) error

UnmarshalValue maps JSON value to passed value. Accepts additional options to customise unmarshal process.

Method supports the same tag and behavior as standard json.Unmarshal method.

Supported additional tags:

- `json:"..."` tag used to collect all orphan values in JSON object to specified field.

Supported special unmarshal types:

- If destination value is jsonreflect.Value, unmarshaler will map original value.

- If destination value is jsonreflect.Unmarshaler, unmarshaler will call Unmarshaler.UnmarshalJSONValue.

Types

type Array

type Array struct {

	// Length is array length
	Length int
	// Items contains items list
	Items []Value
	// contains filtered or unexported fields
}

Array represents JSON items list

func NewArray

func NewArray(items ...Value) *Array

NewArray creates a new array of values

func ToArray

func ToArray(v Value) (*Array, error)

ToArray casts generic value to jsonreflect.Array. Passed value should be object type.

Basically, it's alias to:

val, ok := v.(*Array)

func (Array) Interface

func (arr Array) Interface() interface{}

Interface implements json.Value

func (Array) Ref

func (v Array) Ref() Position

Ref implements jsonreflect.Value

func (Array) String

func (_ Array) String() (string, error)

String implements jsonreflect.Value

func (Array) Type

func (_ Array) Type() Type

Type implements jsonreflect.Value

type Boolean

type Boolean struct {
	Value bool
	// contains filtered or unexported fields
}

Boolean is boolean value

func (Boolean) Interface

func (b Boolean) Interface() interface{}

Interface() implements json.Value

func (Boolean) Ref

func (v Boolean) Ref() Position

Ref implements jsonreflect.Value

func (Boolean) String

func (b Boolean) String() (string, error)

String implements jsonreflect.Value

func (Boolean) Type

func (_ Boolean) Type() Type

Type implements jsonreflect.Value

type GroupedNumbericKeys

type GroupedNumbericKeys []GroupedNumericKey

func (GroupedNumbericKeys) Len

func (gks GroupedNumbericKeys) Len() int

func (GroupedNumbericKeys) Less

func (gks GroupedNumbericKeys) Less(i, j int) bool

func (GroupedNumbericKeys) Swap

func (gks GroupedNumbericKeys) Swap(i, j int)

type GroupedNumericKey

type GroupedNumericKey struct {
	Order []int
	Key   string
}

type MarshalOptions

type MarshalOptions struct {
	// Indent is indentation to apply for output
	Indent string
}

MarshalOptions contains additional marshal options

type Null

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

Null is JSON null value

func (Null) Interface

func (n Null) Interface() interface{}

Interface() implements json.Value

func (Null) Ref

func (v Null) Ref() Position

Ref implements jsonreflect.Value

func (Null) String

func (_ Null) String() (string, error)

String implements jsonreflect.Value

func (Null) Type

func (_ Null) Type() Type

Type implements jsonreflect.Value

type Number

type Number struct {

	// IsFloat is floating point number flag
	IsFloat bool

	// IsSigned is signed number flag
	IsSigned bool
	// contains filtered or unexported fields
}

Number represents json float64 number value

func ToNumber

func ToNumber(v Value, bitSize int) (*Number, error)

ToNumber casts generic value to jsonreflect.Number.

Method only supports number and string values.

func (Number) Float32

func (n Number) Float32() float32

Float32 returns value as float32 number

func (Number) Float64

func (n Number) Float64() float64

Float64 returns value as float64 number

func (Number) Int

func (n Number) Int() int

Int returns value as integer number

func (Number) Int32

func (n Number) Int32() int32

Int32 returns value as int32 number

func (Number) Int64

func (n Number) Int64() int64

Int64 returns value as int64 number

func (Number) Interface

func (n Number) Interface() interface{}

Interface() implements json.Value

func (Number) Ref

func (v Number) Ref() Position

Ref implements jsonreflect.Value

func (Number) String

func (n Number) String() (string, error)

String implements jsonreflect.Value

func (Number) Type

func (_ Number) Type() Type

Type implements jsonreflect.Value

func (Number) Uint

func (n Number) Uint() uint

Uint returns value as unsigned integer number

func (Number) Uint32

func (n Number) Uint32() uint32

Uint32 returns value as uint32 number

func (Number) Uint64

func (n Number) Uint64() uint64

Uint64 returns value as uint64 number

type Object

type Object struct {

	// Items is key-value pair of object values
	Items map[string]Value
	// contains filtered or unexported fields
}

Object represents key-value pair of object field and value

func ToObject

func ToObject(v Value) (*Object, error)

ToObject casts generic value to jsonreflect.Object. Passed value should be object type.

Basically, it's alias to:

val, ok := v.(*Object)

func (Object) GroupNumericKeys

func (o Object) GroupNumericKeys(regex *regexp.Regexp, matchCount int) (GroupedNumbericKeys, error)

GroupNumericKeys groups set of keys with similar numeric prefix or suffix as array by pattern.

Example:

Keys above can be grouped by numeric suffix:

"fan1", "fan2", "fan1_2"

Using this call:

re := regexp.MustCompile(`^fan([\d]+)[_]?([\d]+)?$`)
result, err := obj.GroupNumericKeys(re, 2)
Example
// Group similar keys by numeric suffix.
// Suffix will be used as array index.
//
// see more examples in keys_test.go
re := regexp.MustCompile(`^fan([\d]+)?$`)
src := []byte(`{"fan3": 310, "fan1": 110, "fan2": 210, "foo": "bar"}`)
doc, _ := NewParser(src).Parse()

// Group keys by "fan[0-9]" pattern with regex and only 1 group match
obj := doc.(*Object)
grouped, _ := obj.GroupNumericKeys(re, 1)
for _, item := range grouped {
	fmt.Println(item.Order, item.Key, obj.Items[item.Key].Interface())
}
Output:

[1] fan1 110
[2] fan2 210
[3] fan3 310

func (Object) HasKey

func (o Object) HasKey(keyName string) bool

HasKey checks if key exists in object

func (Object) Interface

func (o Object) Interface() interface{}

Interface() implements json.Value

func (Object) Keys

func (o Object) Keys() []string

Keys returns sorted list of object keys

func (Object) Ref

func (v Object) Ref() Position

Ref implements jsonreflect.Value

func (Object) String

func (_ Object) String() (string, error)

String implements jsonreflect.Value

func (Object) ToMap

func (o Object) ToMap() map[string]interface{}

ToMap returns key-value pair of items as interface value

func (Object) Type

func (_ Object) Type() Type

Type implements jsonreflect.Value

type ParseError

type ParseError struct {
	Position

	Message string
}

func NewInvalidExprError

func NewInvalidExprError(start, end int, val []byte) ParseError

func NewParseError

func NewParseError(pos Position, msg string, args ...interface{}) ParseError

func NewUnexpectedCharacterError

func NewUnexpectedCharacterError(start, end int, char byte) ParseError

func (ParseError) Error

func (p ParseError) Error() string

type Parser

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

Parser is JSON parser

func NewParser

func NewParser(src []byte) *Parser

NewParser creates a new parser instance

func NewParserFromReader

func NewParserFromReader(r io.Reader) (*Parser, error)

NewParserFromReader reads data from passed reader and returns reader instance

func (*Parser) Parse

func (p *Parser) Parse() (Value, error)

Parse parses passed JSON and returns parsed value.

If passed JSON is empty, a nil value returned

Example
// Plain JSON document parsing
data, err := ioutil.ReadFile("testdata/obj_simple.json")
must(err)

// or NewParserFromReader() to read from io.Reader
result, err := NewParser(data).Parse()
must(err)

// every json.Value has .Interface() method
fmt.Println(result.Interface())

// iterate over object
obj := result.(*Object)
for k, v := range obj.Items {
	fmt.Printf("- %s %T: %v\n", k, v, v.Interface())
}

// or simply return map
fmt.Println(obj.ToMap())
Output:

type Position

type Position struct {
	Start int
	End   int
}

type String

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

String represents JSON string

func (String) Interface

func (s String) Interface() interface{}

Interface() implements json.Value

func (String) Number

func (s String) Number() (*Number, error)

Number returns number quoted in string

func (String) RawString

func (s String) RawString() string

RawString returns quoted raw string

func (String) Ref

func (v String) Ref() Position

Ref implements jsonreflect.Value

func (String) String

func (s String) String() (string, error)

String implements jsonreflect.Value

func (String) Type

func (_ String) Type() Type

Type implements jsonreflect.Value

type Type

type Type uint

Type represents value type

const (
	// TypeUnknown is invalid value type
	TypeUnknown Type = iota

	// TypeNull is null value type
	TypeNull

	// TypeBoolean is boolean value type
	TypeBoolean

	// TypeNumber is number value type
	TypeNumber

	// TypeString is string value type
	TypeString

	// TypeObject is object value type
	TypeObject

	// TypeArray is array value type
	TypeArray
)

func TypeOf

func TypeOf(v Value) Type

TypeOf returns value type.

Returns TypeNull if nil value passed.

func (Type) String

func (t Type) String() string

String returns value type as string

type UnmarshalOption

type UnmarshalOption func(fn *unmarshalParams)

UnmarshalOption is unmarshal option

var (
	// NoStrict disables unmarshal strict mode.
	//
	// When strict mode is disabled, unmarshaler will try to cast JSON value
	// to destination value.
	//
	// Supported possible casts (without strict mode):
	//	// If destination type is numeric, unmarshaler will try
	//	// to parse source value as number.
	//	string -> any numeric value
	//
	//	// Any numeric value can be casted to string
	//  any numeric value -> string
	//
	//	// Any valid boolean can be casted to string (and vice versa)
	//	boolean <-> string
	//
	NoStrict UnmarshalOption = func(fn *unmarshalParams) {
		fn.strict = false
	}

	// DangerouslySetPrivateFields allows unmarshaler to modify private fields
	// which have `json` tag.
	//
	// Use it if you really know what to do, you have been warned.
	//
	// We are not responsible for corrupted memory, dead hard drives, thermonuclear war,
	// or you getting fired because the production database went down.
	//
	// Please do some research if you have any concerns about this option.
	DangerouslySetPrivateFields UnmarshalOption = func(fn *unmarshalParams) {
		fn.dangerouslySetPrivateFields = true
	}
)

type Unmarshaler

type Unmarshaler interface {
	UnmarshalJSONValue(v Value) error
}

Unmarshaler is the interface implemented by types that can unmarshal a JSON value description of themselves.

type Value

type Value interface {
	// Ref returns reference to value in source
	Ref() Position

	// Type returns value type
	Type() Type

	// Interface returns interface{} value
	Interface() interface{}

	// String returns string representation of a value
	String() (string, error)
	// contains filtered or unexported methods
}

Value is abstract JSON document value

func ValueOf

func ValueOf(src []byte) (Value, error)

ValueOf parses the JSON-encoded data and returns a document structure.

Alias to NewParser().Parse()

Directories

Path Synopsis
internal
testutil
package testutil is internal test helpers package
package testutil is internal test helpers package

Jump to

Keyboard shortcuts

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