From be7b154d093db491c61285049f46770a9075bc15 Mon Sep 17 00:00:00 2001 From: Charles Banning Date: Fri, 14 Sep 2018 13:42:46 -0600 Subject: [PATCH] Add/refactor wrapper functions This was inspired by https://www.reddit.com/r/golang/comments/9eclgy/xml_unmarshaling_internal_references/ --- examples/reddit02.go | 61 ++++++++++++++++++++++++++++++++++++++++++++ keyvalues.go | 22 ++++++++++++++-- keyvalues_test.go | 46 +++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 examples/reddit02.go diff --git a/examples/reddit02.go b/examples/reddit02.go new file mode 100644 index 0000000..ab7dc98 --- /dev/null +++ b/examples/reddit02.go @@ -0,0 +1,61 @@ +// https://www.reddit.com/r/golang/comments/9eclgy/xml_unmarshaling_internal_references/ + +package main + +import ( + "fmt" + + "github.com/clbanning/mxj" +) + +var data = []byte(` + + + + + + Hello!! + +`) + +func main() { + m, err := mxj.NewMapXml(data) + if err != nil { + fmt.Println("err:", err) + return + } + fmt.Printf("%v\n", m) + + type mystruct struct { + FromUser string + ToUser string + Message string + } + myStruct := mystruct{} + val, err := m.ValueForKey("user", "-id:1") + if val != nil { + myStruct.FromUser = val.(map[string]interface{})["-name"].(string) + } else { + // if there no val, then err is at least KeyNotExistError + fmt.Println("err:", err) + return + } + val, err = m.ValueForKey("user", "-id:2") + if val != nil { + myStruct.ToUser = val.(map[string]interface{})["-name"].(string) + } else { + // if there no val, then err is at least KeyNotExistError + fmt.Println("err:", err) + return + } + val, err = m.ValueForKey("#text") + if val != nil { + myStruct.Message = val.(string) + } else { + // if there no val, then err is at least KeyNotExistError + fmt.Println("err:", err) + return + } + + fmt.Printf("%#v\n", myStruct) +} diff --git a/keyvalues.go b/keyvalues.go index 968080f..40acc3f 100644 --- a/keyvalues.go +++ b/keyvalues.go @@ -60,6 +60,21 @@ func (mv Map) ValuesForKey(key string, subkeys ...string) ([]interface{}, error) return ret[:cnt], nil } +var KeyNotExistError = errors.New("Key does not exist") + +// ValueForKey is a wrapper on ValuesForKey. It returns the first member of []interface{}, if any. +// If there is no value, "nil, nil" is returned. +func (mv Map) ValueForKey(key string, subkeys ...string) (interface{}, error) { + vals, err := mv.ValuesForKey(key, subkeys...) + if err != nil { + return nil, err + } + if len(vals) == 0 { + return nil, KeyNotExistError + } + return vals[0], nil +} + // hasKey - if the map 'key' exists append it to array // if it doesn't do nothing except scan array and map values func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys map[string]interface{}) { @@ -615,14 +630,17 @@ func hasKeyPath(crumbs string, iv interface{}, key string, basket map[string]boo } } -// Returns the first found value for the path. +var PathNotExistError = errors.New("Path does not exist") + +// ValueForPath wrap ValuesFor Path and returns the first value returned. +// If no value is found it returns 'nil' and PathNotExistError. func (mv Map) ValueForPath(path string) (interface{}, error) { vals, err := mv.ValuesForPath(path) if err != nil { return nil, err } if len(vals) == 0 { - return nil, errors.New("ValueForPath: path not found") + return nil, PathNotExistError } return vals[0], nil } diff --git a/keyvalues_test.go b/keyvalues_test.go index 46df6eb..b2e185c 100644 --- a/keyvalues_test.go +++ b/keyvalues_test.go @@ -444,3 +444,49 @@ func TestValueForPathString(t *testing.T) { t.Fatal("wrong value") } } + +func TestValueForPathError(t *testing.T) { + m := map[string]interface{}{ + "Div": map[string]interface{}{ + "Colour": "blue", + }, + } + mv := Map(m) + + _, err := mv.ValueForPath("Color") + if err != PathNotExistError { + t.Fatal("no PathNotExistError returned") + } +} + +func TestValueForKey(t *testing.T) { + m := map[string]interface{}{ + "Div": map[string]interface{}{ + "Colour": "blue", + }, + } + mv := Map(m) + + v, err := mv.ValueForKey("Colour") + if err != nil { + t.Fatal(err) + } + if str, ok := v.(string); !ok || str != "blue" { + t.Fatal("wrong value") + } +} + +func TestValueForKeyError(t *testing.T) { + m := map[string]interface{}{ + "Div": map[string]interface{}{ + "Colour": "blue", + }, + } + mv := Map(m) + + _, err := mv.ValueForKey("Color") + if err != KeyNotExistError { + t.Fatal("no KeyNotExistError returned") + } +} +