Documentation ¶
Overview ¶
Package decode implements a generic interface{} decoder. It allows implementing custom YAML/JSON decoding logic only once. Instead of implementing UnmarshalYAML and UnmarshalJSON differently twice, you would implement Decode once, parse the YAML/JSON input into a map[string]interface{} and decode it using this package.
var data map[string]interface{} if err := json.Decode(&data, input); err != nil { log.Fatal(err) } var result MyStruct if err := decode.Decode(&result, data); err != nil { log.Fatal(err) }
This also makes it possible to implement custom markup parsing and deserialization strategies that get decoded into a user-provided struct.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Decode ¶
func Decode(dest, src interface{}) error
Decode from src into dest where dest is a pointer to the value being decoded.
Primitives are mapped as-is with pointers created or dereferenced as necessary. Maps and slices use Decode recursively for each of their items. For structs, the source must be a map[string]interface{} or map[interface{}]interface{}. Each key in the map calls Decode recursively with the field of the struct that has a name similar to the key (case insensitive match).
var item struct{ Key, Value string } err := Decode(&item, map[string]string{"key": "some key", "Value": "some value"})
The name of the field in the map may be customized with the `config` tag.
var item struct { Key string `config:"name"` Value string } var item struct{ Key, Value string } err := Decode(&item, map[string]string{"name": "token", "Value": "some value"})
The destination type or any subtype may implement the Decoder interface to customize how it gets decoded.
Example ¶
type Item struct { Key string `config:"name"` Value string } var item Item err := Decode(&item, map[string]interface{}{ "name": "foo", "value": "bar", }) if err != nil { log.Fatal(err) } fmt.Println(item.Key, item.Value)
Output: foo bar
Example (Decoder) ¶
var ss StringSet err := Decode(&ss, []interface{}{"foo", "bar", "foo", "baz"}) if err != nil { log.Fatal(err) } var items []string for item := range ss { items = append(items, item) } sort.Strings(items) fmt.Println(items)
Output: [bar baz foo]
Types ¶
type Decoder ¶
type Decoder interface { // Decode receives a function that will attempt to decode the source data // into the given target. The argument to Into MUST be a pointer to the // target object. Decode(Into) error }
Decoder is any type which has custom decoding logic. Types may implement Decode and rely on the given Into function to read values into a different shape, validate the result, and fill themselves with it.
For example the following lets users provide a list of strings to decode a set.
type StringSet map[string]struct{} func (ss *StringSet) Decode(into decode.Into) error { var items []string if err := into(&items); err != nil { return err } *ss = make(map[string]struct{}) for _, item := range items { (*ss)[item] = struct{}{} } return nil }
type Into ¶
type Into func(dest interface{}) error
Into is a function that attempts to decode the source data into the given shape.
Types that implement Decoder are provided a reference to an Into object so that they can decode a different shape, validate the result and populate themselves with the result.
var values []string err := into(&value) for _, value := range values { if value == "reserved" { return errors.New(`a value in the list cannot be "reserved"`) } self.Values = append(self.Values, value) }
The function is safe to call multiple times if you need to try to decode different shapes. For example,
// Allow the user to just use the string "default" for the default // configuration. var name string if err := into(&name); err == nil { if name == "default" { *self = DefaultConfiguration return } return fmt.Errorf("unknown name %q", name) } // Otherwise, the user must provide {someAttr: "value"} as the input for // explicit configuration. var custom struct{ SomeAttr string } if err := into(&custom); err != nil { return err } self.SomeAttr = custom return nil
If the destination type or any sub-type implements Decoder, that function will be called. This means that Into MUST NOT be called on the type whose Decode function is currently running or this will end up in an infinite loop.