Documentation ¶
Overview ¶
Access nested properties in Go data structures with Data Queries
Concepts ¶
A query result is (res any, stat Status, err error), where res is the queried value, stat says whether res was found and err says whether something went wrong.
Use assert functions or fallback functions to easily use query results with an expected type.
Use As to create an assertion function from a convert function.
Use Val to create a fallback function from a convert function.
The convert function for target type T is func(v any) (r T, err error). If conversion of v fails, err != nil and T's zero value. Otherwise, r is the conversion result.
By convention, convert functions for target type <T> are named To<T>(). Predefined assert functions are named As<T>(), predefined fallback functions <T>Or().
Use Get as the general way to perform queries. Read on to find more specific and efficient ways to query:
Index, Field, Key to step with int, string and any.
And also SliceAny, DictAny, MapAny to get query results that can be asserted from typical generic Go data.
To extend daq to your own types write convert functions and familiarize with Stepper, Slice, Dict and Map.
Query Results ¶
A query result is a multi-value return of the form (res any, stat Status, err error). This triple is designed to be accepted by many of daq's functions to create a convenient syntax.
For successful queries – single or multi-level – res is the result value of the query.
For successful queries, stat is 0. Otherwise, stat indicates the number of steps that have not yet been successfully executed. For a valid query it is not considered an error when nothing is found.
Executing invalid queries, e.g. indexing a slice with a string, leads to err != nil. Then err describes the problem.
For queries, stat==0 => err==nil must apply. Assert or fallback functions may deviate from this.
Convert Functions for Go's numeric types ¶
Signed and unsigned integer types are promoted to each other as long as the actual runtime value is in target type's range.
Float values are promoted to integer types if the actual float has no fraction, and it is in target type's range.
Complex values with imag()==0 are promoted to integer by promoting the real() part according to the float rules.
Example ¶
var data any json.Unmarshal([]byte(`{ "a": 3.1415, "bs": [1, 2, 3] }`), &data) prop, rest, _ := Get(data, "a") // Ignore error in example for brevity fmt.Println(prop, rest) prop, rest, err := Get(data, "b") // This will fail, no "b" fmt.Println(prop, rest, err) x, rest, _ := AsFloat64(Get(data, "bs", 1)) // Type assertion (encoding/json uses float64 for numbers) fmt.Println(x, rest) x, rest, _ = AsFloat64(Get(data, "bs", -1)) // Access backwards from end fmt.Println(x, rest) x = Float64Or(-1)(Get(data, "bs", 3)) // Fallback to -1 if Get fails fmt.Println(x)
Output: 3.1415 0 map[a:3.1415 bs:[1 2 3]] 1 <nil> 2 0 3 0 -1
Example (Readme_daq) ¶
var data any json.Unmarshal([]byte(`{ "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "GlossTerm": "Standard Generalized Markup Language" } } } } }`), &data) title := StringOr("-no title-")(Get(data, "glossary", "title")) fmt.Println(title)
Output: example glossary
Example (Readme_plainGo) ¶
var data any json.Unmarshal([]byte(`{ "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "GlossTerm": "Standard Generalized Markup Language" } } } } }`), &data) title := "-no title-" jObj, ok := data.(map[string]any) if !ok { fmt.Println(title) return } if data = jObj["glossary"]; data == nil { fmt.Println(title) return } if jObj, ok = data.(map[string]any); !ok { fmt.Println(title) return } if data = jObj["title"]; data == nil { fmt.Println(title) return } title, _ = data.(string) fmt.Println(title)
Output: example glossary
Index ¶
- func As[T any](conv func(any) (T, error)) func(any, Status, error) (T, Status, error)
- func AsDict(v any, stat Status, err error) (Dict, Status, error)
- func AsMap(v any, stat Status, err error) (Map, Status, error)
- func AsSlice(v any, stat Status, err error) (Slice, Status, error)
- func BoolOr(fallback bool) func(any, Status, error) bool
- func Complex128Or(fallback complex128) func(any, Status, error) complex128
- func Complex64Or(fallback complex64) func(any, Status, error) complex64
- func DeepEqual(lhs, rhs any) bool
- func DeepEqualFunc(lhs, rhs any, eq func(any, any) bool) bool
- func DictOr(fallback Dict) func(any, Status, error) Dict
- func DurationOr(fallback time.Duration) func(any, Status, error) time.Duration
- func Float32Or(fallback float32) func(any, Status, error) float32
- func Float64Or(fallback float64) func(any, Status, error) float64
- func GetTrack(data any, path ...any) (t Track, stat Status, err error)
- func Int16Or(fallback int16) func(any, Status, error) int16
- func Int32Or(fallback int32) func(any, Status, error) int32
- func Int64Or(fallback int64) func(any, Status, error) int64
- func Int8Or(fallback int8) func(any, Status, error) int8
- func IntOr(fallback int) func(any, Status, error) int
- func MapOr(fallback Map) func(any, Status, error) Map
- func Must[T any](res T, stat Status, err error) T
- func SliceOr(fallback Slice) func(any, Status, error) Slice
- func StepEach(data any, do func(kind reflect.Kind, key, value any) error) error
- func StringOr(fallback string) func(any, Status, error) string
- func TestingDeepEqual(t testing.TB, lhs, rhs any) (diffCount int)
- func TestingDeepEqualFunc(t testing.TB, lhs, rhs any, eq func(any, any) bool) (diffCount int)
- func TimeOr(fallback time.Time) func(any, Status, error) time.Time
- func ToBool(v any) (bool, error)
- func ToComplex128(v any) (complex128, error)
- func ToComplex64(v any) (complex64, error)
- func ToDuration(v any) (time.Duration, error)
- func ToFloat32(v any) (float32, error)
- func ToFloat64(v any) (float64, error)
- func ToInt(v any) (int, error)
- func ToInt16(v any) (int16, error)
- func ToInt32(v any) (int32, error)
- func ToInt64(v any) (int64, error)
- func ToInt8(v any) (int8, error)
- func ToString(v any) (string, error)
- func ToTime(v any) (time.Time, error)
- func ToUint(v any) (uint, error)
- func ToUint16(v any) (uint16, error)
- func ToUint32(v any) (uint32, error)
- func ToUint64(v any) (uint64, error)
- func ToUint8(v any) (uint8, error)
- func ToUintPtr(v any) (uintptr, error)
- func Uint16Or(fallback uint16) func(any, Status, error) uint16
- func Uint32Or(fallback uint32) func(any, Status, error) uint32
- func Uint64Or(fallback uint64) func(any, Status, error) uint64
- func Uint8Or(fallback uint8) func(any, Status, error) uint8
- func UintOr(fallback uint) func(any, Status, error) uint
- func UintPtrOr(fallback uintptr) func(any, Status, error) uintptr
- func Val[T any](conv func(any) (T, error), fallback T) func(any, Status, error) T
- type Alt
- type CollectionTypeError
- type Delta
- type Dict
- type DictAny
- type FirstIdxOf
- type FirstKeyOf
- type FirstNameOf
- type FirstOf
- type Map
- type MapAny
- type NotFound
- type Slice
- type SliceAny
- type Status
- func AsBool(v any, stat Status, err error) (bool, Status, error)
- func AsComplex128(v any, stat Status, err error) (complex128, Status, error)
- func AsComplex64(v any, stat Status, err error) (complex64, Status, error)
- func AsDuration(v any, stat Status, err error) (time.Duration, Status, error)
- func AsFloat32(v any, stat Status, err error) (float32, Status, error)
- func AsFloat64(v any, stat Status, err error) (float64, Status, error)
- func AsInt(v any, stat Status, err error) (int, Status, error)
- func AsInt16(v any, stat Status, err error) (int16, Status, error)
- func AsInt32(v any, stat Status, err error) (int32, Status, error)
- func AsInt64(v any, stat Status, err error) (int64, Status, error)
- func AsInt8(v any, stat Status, err error) (int8, Status, error)
- func AsString(v any, stat Status, err error) (string, Status, error)
- func AsTime(v any, stat Status, err error) (time.Time, Status, error)
- func AsUint(v any, stat Status, err error) (uint, Status, error)
- func AsUint16(v any, stat Status, err error) (uint16, Status, error)
- func AsUint32(v any, stat Status, err error) (uint32, Status, error)
- func AsUint64(v any, stat Status, err error) (uint64, Status, error)
- func AsUint8(v any, stat Status, err error) (uint8, Status, error)
- func AsUintPtr(v any, stat Status, err error) (uintptr, Status, error)
- func Field(data any, name string) (res any, stat Status, err error)
- func Get(data any, path ...any) (res any, stat Status, err error)
- func Index(data any, index int) (res any, stat Status, err error)
- func Key(data, key any) (res any, stat Status, err error)
- func OK(res any, stat Status, err error) (any, Status, error)
- func Step(data, step any) (res any, stat Status, err error)
- type StepError
- type Stepper
- type Time
- type Track
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func As ¶ added in v0.4.1
As returns the assert function for the specified type conversion conv to type T. Assert functions take query results as input. In any case, assert functions
return the status of the query result unchanged.
never return error==nil if the query result status was not OK. If necessary, a [NoFound] error is used.
If the query result already had an error, As returns the null value of T and the error. Otherwise it perform the conversion – even if the input statis was not OK.
This means that error==nil is only returned if the conversion of a complete query is successfully converted.
Example ¶
data := struct { Foo int Bar string }{ Foo: 4711, Bar: "baz", } fmt.Println(As(ToString)(Get(data, "Foo"))) fmt.Println(As(ToString)(Get(data, "Bar"))) fmt.Println(As(ToString)(Get(data, "void")))
Output: 0 cannot return int as string baz 0 <nil> 1 cannot return struct { Foo int; Bar string } as string
func AsSlice ¶
Example ¶
s := SliceOr(nil)(DictAny(testExample).Get("a")) for i := 0; i < s.DaQLen(); i++ { res, rest, err := s.DaQGet(i) must.Do(err) fmt.Println(res, rest) }
Output: map[b:map[c:3] d:foo] 0 false 0
func Complex128Or ¶ added in v0.6.0
func Complex128Or(fallback complex128) func(any, Status, error) complex128
func Complex64Or ¶ added in v0.6.0
func Must ¶ added in v0.6.0
Must checks stat to be OK using OK and panics if the resulting error is not nil. Otherwiese Must returns res.
func StepEach ¶ added in v0.3.0
Example (Array) ¶
data := [4]any{0, '1', "two", nil} err := StepEach(data, testPrintStep) fmt.Println(err)
Output: kind 'array': key=0 / value=0 kind 'array': key=1 / value=49 kind 'array': key=2 / value="two" kind 'array': key=3 / value=<nil> <nil>
Example (Atom) ¶
err := StepEach(5180, testPrintStep) fmt.Println(err)
Output: kind 'invalid': key=<nil> / value=5180 <nil>
Example (Map) ¶
data := map[any]any{ false: "foo", 7: nil, "bar": 4711, } err := StepEach(data, testPrintStep) fmt.Println(err)
Output: kind 'map': key=false / value="foo" kind 'map': key=7 / value=<nil> kind 'map': key="bar" / value=4711 <nil>
Example (Slice) ¶
data := []any{0, '1', "two", nil} err := StepEach(data, testPrintStep) fmt.Println(err)
Output: kind 'slice': key=0 / value=0 kind 'slice': key=1 / value=49 kind 'slice': key=2 / value="two" kind 'slice': key=3 / value=<nil> <nil>
Example (Struct) ¶
type Tstnest struct { Foo int } data := struct { Tstnest Bar string Nest Tstnest }{ Tstnest: Tstnest{Foo: 4711}, Bar: "baz", Nest: Tstnest{Foo: -1}, } err := StepEach(data, testPrintStep) fmt.Println(err)
Output: kind 'struct': key="Foo" / value=4711 kind 'struct': key="Bar" / value="baz" kind 'struct': key="Nest" / value=daq.Tstnest{Foo:-1} <nil>
func TestingDeepEqual ¶ added in v0.5.0
func TestingDeepEqualFunc ¶ added in v0.5.0
func ToComplex128 ¶ added in v0.6.0
func ToComplex128(v any) (complex128, error)
func ToComplex64 ¶ added in v0.6.0
func ToTime ¶ added in v0.4.1
Example ¶
t := time.Date(2023, 12, 29, 2, 4, 0, 0, time.UTC) fmt.Println(ToTime(t)) fmt.Println(ToTime(&t)) fmt.Println(ToTime((*time.Time)(nil)))
Output: 2023-12-29 02:04:00 +0000 UTC <nil> 2023-12-29 02:04:00 +0000 UTC <nil> 0001-01-01 00:00:00 +0000 UTC <nil>
func Val ¶
Val returns a fallback function for the given type conversion conv. A fallback function always returns a value of expected type T. It returns the asserted value of As in case of success, i.e. status OK and err==nil. Otherwise it returns the fallback.
Example ¶
data := struct { Foo int Bar string }{ Foo: 4711, Bar: "baz", } fmt.Println(Val(ToString, "fallback1")(Get(data, "Foo"))) fmt.Println(Val(ToString, "fallback2")(Get(data, "Bar"))) fmt.Println(Val(ToString, "fallback3")(Get(data, "void")))
Output: fallback1 baz fallback3
Types ¶
type Alt ¶
Example ¶
data := []string{"foo", "bar", "baz"} fmt.Println(Step(data, "bar")) fmt.Println(Step(data, Alt{S: "bar", Or: 4711}))
Output: <nil> 1 key lookup: cannot use []string as daq.Map 4711 0 <nil>
type CollectionTypeError ¶ added in v0.5.0
type CollectionTypeError struct {
// contains filtered or unexported fields
}
func (CollectionTypeError) Error ¶ added in v0.5.0
func (e CollectionTypeError) Error() string
func (CollectionTypeError) Is ¶ added in v0.5.0
func (e CollectionTypeError) Is(err error) bool
type Delta ¶ added in v0.5.0
type Dict ¶ added in v0.4.1
type Dict interface { DaQGet(name string) (res any, stat Status, err error) DaQEachKey(do func(key string) error) error }
Dict is queried with a string as name.
type DictAny ¶ added in v0.4.1
func (DictAny) DaQEachKey ¶ added in v0.4.1
type FirstIdxOf ¶ added in v0.5.0
type FirstIdxOf []int
Example ¶
data := []string{"foo", "bar", "baz"} fmt.Println(Step(data, FirstIdxOf{99, 1})) // more efficient fmt.Println(Step(data, FirstOf{99, 1})) // more general
Output: bar 0 <nil> bar 0 <nil>
type FirstKeyOf ¶ added in v0.5.0
type FirstKeyOf []any
Example ¶
data := map[any]any{ "foo": 1, "bar": 2, "baz": 3, } fmt.Println(Step(data, FirstKeyOf{"quux", "bar"})) // more efficient fmt.Println(Step(data, FirstOf{"quux", "bar"})) // more general
Output: 2 0 <nil> 2 0 <nil>
type FirstNameOf ¶
type FirstNameOf []string
Example ¶
data := map[string]int{ "foo": 1, "bar": 2, "baz": 3, } fmt.Println(Step(data, FirstNameOf{"quux", "bar"})) // more efficient fmt.Println(Step(data, FirstOf{"quux", "bar"})) // more general
Output: 2 0 <nil> 2 0 <nil>
type Map ¶
type Map interface { DaQGet(key any) (res any, stat Status, err error) DaQEachKey(do func(key any) error) error }
Map is queried with any comparable value as key.
type Slice ¶
Slice is queried with an int index. Negative indices access from the end with -1 being the last element and -DaQLen() the first.
type Status ¶ added in v0.6.0
type Status int
func AsBool ¶
Example ¶
fmt.Println(AsBool(Get(true))) fmt.Println(BoolOr(false)(Get(4711)))
Output: true 0 <nil> false
func AsComplex128 ¶ added in v0.6.0
func AsComplex64 ¶ added in v0.6.0
func AsString ¶
Example ¶
fmt.Println(StringOr("-")(DictAny(testExample).Get("a"))) fmt.Println(Val(ToString, "-")(Get(testExample, "a", 0, "d")))
Output: - foo
func Get ¶
Get queries data for an element res by using the path of steps. See Step and Stepper from details on steps. Get terminates the query if a step is not successful. Get returns the last obtained result, the number of pending steps and, if applicable, the error of the last step.
Example ¶
data := map[string]any{ "a": []any{ map[string]any{ "b": map[any]any{"c": 3}, "d": "foo", }, false, }, } var i int = IntOr(-1)(Get(data, "a", 0, "b", "c")) fmt.Println(i) fmt.Println(Get(data, "a", 0, "b", "d")) fmt.Println(Get(nil, "a", 0, "b", "d"))
Output: 3 map[c:3] 1 <nil> <nil> 4 <nil>
func OK ¶ added in v0.6.0
OK checks valid query results (err==nil) to be OK. If stat is not OK, it return res, stat and the corresponding NotFound error. Otherwise OK returns its input unchanged.
func Step ¶
Step does the step from a data value to an element value res. The Go types int and string are always acceped as steps in a query.
- With int you can go one step from Slice or Map to an element.
- With string you can go one step from Dict or Map to an element.
It is not considered an error if data does not have an element matching step. For a successful step, where data has a matching element, Step returns stat==0, otherwise stat==1.
type Stepper ¶
type Stepper interface { // stat == 0 => err == nil Step(data any) (res any, stat Status, err error) }
Stepper allows to define how Step does the step from a data value to an element value. Implement Stepper for your own strategy to navigate your data.
type Time ¶
type Time string
Example ¶
data := struct { Time string }{ Time: "2023-12-29T22:04:00Z", } // Asserting time.Time needs parsing, so we use Time() to create a parsing assertion fmt.Println(Val(Time(time.RFC3339).To, time.Time{})(Get(data, "Time"))) fmt.Println(Val(Time(time.RFC3339).To, time.Time{})(Get(data, "void")))
Output: 2023-12-29 22:04:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC