From 5d0090c18c8628be7c896c55496170acdb764190 Mon Sep 17 00:00:00 2001 From: bidong Date: Thu, 16 Oct 2014 19:46:07 +0800 Subject: [PATCH 1/4] add tagNameFunc to handle complicated tagName --- reflectx/reflect.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/reflectx/reflect.go b/reflectx/reflect.go index 847b7600..b2dfa91b 100644 --- a/reflectx/reflect.go +++ b/reflectx/reflect.go @@ -19,10 +19,11 @@ type fieldMap map[string][]int // behaves like most marshallers, optionally obeying a field tag for name // mapping and a function to provide a basic mapping of fields to names. type Mapper struct { - cache map[reflect.Type]fieldMap - tagName string - mapFunc func(string) string - mutex sync.Mutex + cache map[reflect.Type]fieldMap + tagName string + mapTagNameFunc func(string, string) string + mapFunc func(string) string + mutex sync.Mutex } // NewMapper returns a new mapper which optionally obeys the field tag given @@ -34,6 +35,16 @@ func NewMapper(tagName string) *Mapper { } } +// NewMapper returns a new mapper which optionally obeys the field tag given +// by tagName. If tagName is the empty string, it is ignored. +func NewMapperTagNameFunc(tagName string, f func(string, string) string) *Mapper { + return &Mapper{ + cache: make(map[reflect.Type]fieldMap), + tagName: tagName, + mapTagNameFunc: f, + } +} + // NewMapperFunc returns a new mapper which optionally obeys a field tag and // a struct field name mapper func given by f. Tags will take precedence, but // for any other field, the mapped name will be f(field.Name) @@ -51,7 +62,7 @@ func (m *Mapper) TypeMap(t reflect.Type) fieldMap { m.mutex.Lock() mapping, ok := m.cache[t] if !ok { - mapping = getMapping(t, m.tagName, m.mapFunc) + mapping = getMapping(t, m.tagName, m.mapTagNameFunc, m.mapFunc) m.cache[t] = mapping } m.mutex.Unlock() @@ -201,7 +212,7 @@ func apnd(is []int, i int) []int { // getMapping returns a mapping for the t type, using the tagName and the mapFunc // to determine the canonical names of fields. -func getMapping(t reflect.Type, tagName string, mapFunc func(string) string) fieldMap { +func getMapping(t reflect.Type, tagName string, mapTagNameFunc func(string, string) string, mapFunc func(string) string) fieldMap { queue := []typeQueue{} queue = append(queue, typeQueue{Deref(t), []int{}}) m := fieldMap{} @@ -220,6 +231,10 @@ func getMapping(t reflect.Type, tagName string, mapFunc func(string) string) fie } else { name = f.Name } + } else { + if mapTagNameFunc != nil { + name = mapTagNameFunc(tagName, name) + } } // if the name is "-", disabled via a tag, skip it From 7bec454da01bdc47ae0b9376346e5d734545e35b Mon Sep 17 00:00:00 2001 From: bidong Date: Fri, 17 Oct 2014 17:20:31 +0800 Subject: [PATCH 2/4] fix naming; add test code. --- reflectx/reflect.go | 14 ++++++-------- reflectx/reflect_test.go | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/reflectx/reflect.go b/reflectx/reflect.go index b2dfa91b..de0e875a 100644 --- a/reflectx/reflect.go +++ b/reflectx/reflect.go @@ -21,7 +21,7 @@ type fieldMap map[string][]int type Mapper struct { cache map[reflect.Type]fieldMap tagName string - mapTagNameFunc func(string, string) string + tagNameMapFunc func(string, string) string mapFunc func(string) string mutex sync.Mutex } @@ -41,7 +41,7 @@ func NewMapperTagNameFunc(tagName string, f func(string, string) string) *Mapper return &Mapper{ cache: make(map[reflect.Type]fieldMap), tagName: tagName, - mapTagNameFunc: f, + tagNameMapFunc: f, } } @@ -62,7 +62,7 @@ func (m *Mapper) TypeMap(t reflect.Type) fieldMap { m.mutex.Lock() mapping, ok := m.cache[t] if !ok { - mapping = getMapping(t, m.tagName, m.mapTagNameFunc, m.mapFunc) + mapping = getMapping(t, m.tagName, m.tagNameMapFunc, m.mapFunc) m.cache[t] = mapping } m.mutex.Unlock() @@ -212,7 +212,7 @@ func apnd(is []int, i int) []int { // getMapping returns a mapping for the t type, using the tagName and the mapFunc // to determine the canonical names of fields. -func getMapping(t reflect.Type, tagName string, mapTagNameFunc func(string, string) string, mapFunc func(string) string) fieldMap { +func getMapping(t reflect.Type, tagName string, tagNameMapFunc func(string, string) string, mapFunc func(string) string) fieldMap { queue := []typeQueue{} queue = append(queue, typeQueue{Deref(t), []int{}}) m := fieldMap{} @@ -231,10 +231,8 @@ func getMapping(t reflect.Type, tagName string, mapTagNameFunc func(string, stri } else { name = f.Name } - } else { - if mapTagNameFunc != nil { - name = mapTagNameFunc(tagName, name) - } + } else if tagNameMapFunc != nil { + name = tagNameMapFunc(tagName, name) } // if the name is "-", disabled via a tag, skip it diff --git a/reflectx/reflect_test.go b/reflectx/reflect_test.go index e0a40f67..e8068ac1 100644 --- a/reflectx/reflect_test.go +++ b/reflectx/reflect_test.go @@ -68,6 +68,29 @@ func TestEmbedded(t *testing.T) { } } +func TestTagNameMapping(t *testing.T) { + type Strategy struct { + StrategyId string `protobuf:"bytes,1,opt,name=strategy_id" json:"strategy_id,omitempty"` + StrategyName string + } + + m := NewMapperTagNameFunc("json", func(tagName string, value string) string { + if strings.Contains(value, ",") { + return strings.Split(value, ",")[0] + } else { + return value + } + }) + strategy := Strategy{"1", "Alpah"} + mapping := m.TypeMap(reflect.TypeOf(strategy)) + + for _, key := range []string{"strategy_id", "StrategyName"} { + if _, ok := mapping[key]; !ok { + t.Errorf("Expecting to find key %s in mapping but did not.", key) + } + } +} + func TestMapping(t *testing.T) { type Person struct { ID int From fce14bc7c0df1f9b6e0fbb324dbfe8eb3040df2e Mon Sep 17 00:00:00 2001 From: bidong Date: Sun, 14 Dec 2014 14:46:41 +0800 Subject: [PATCH 3/4] try to add comments for NewMapperTagNameFunc --- reflectx/reflect.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/reflectx/reflect.go b/reflectx/reflect.go index de0e875a..60299c94 100644 --- a/reflectx/reflect.go +++ b/reflectx/reflect.go @@ -35,8 +35,9 @@ func NewMapper(tagName string) *Mapper { } } -// NewMapper returns a new mapper which optionally obeys the field tag given -// by tagName. If tagName is the empty string, it is ignored. +// NewMapperTagNameFunc returns a new mapper which optionally obeys a field tag and +// a tag name mapper func given by f. For each field, the mapped name will be f(tagName, extraParam ) +// if function f is not nil. func NewMapperTagNameFunc(tagName string, f func(string, string) string) *Mapper { return &Mapper{ cache: make(map[reflect.Type]fieldMap), @@ -210,7 +211,7 @@ func apnd(is []int, i int) []int { return x } -// getMapping returns a mapping for the t type, using the tagName and the mapFunc +// getMapping returns a mapping for the t type, using the tagName, tagNameMapFunc and the mapFunc // to determine the canonical names of fields. func getMapping(t reflect.Type, tagName string, tagNameMapFunc func(string, string) string, mapFunc func(string) string) fieldMap { queue := []typeQueue{} From 621e1eea4369e1db791aeb12664c971a4ea9046a Mon Sep 17 00:00:00 2001 From: Jason Moiron Date: Sat, 10 Jan 2015 10:23:00 -0500 Subject: [PATCH 4/4] change the tag map API slightly so that it is identical to the regular map funcs (the tag name is not important) --- reflectx/reflect.go | 37 +++++++++++++++++++------------------ reflectx/reflect_test.go | 4 ++-- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/reflectx/reflect.go b/reflectx/reflect.go index 55f9aece..d49b7bcd 100644 --- a/reflectx/reflect.go +++ b/reflectx/reflect.go @@ -19,11 +19,11 @@ type fieldMap map[string][]int // behaves like most marshallers, optionally obeying a field tag for name // mapping and a function to provide a basic mapping of fields to names. type Mapper struct { - cache map[reflect.Type]fieldMap - tagName string - tagNameMapFunc func(string, string) string - mapFunc func(string) string - mutex sync.Mutex + cache map[reflect.Type]fieldMap + tagName string + tagMapFunc func(string) string + mapFunc func(string) string + mutex sync.Mutex } // NewMapper returns a new mapper which optionally obeys the field tag given @@ -35,14 +35,15 @@ func NewMapper(tagName string) *Mapper { } } -// NewMapperTagNameFunc returns a new mapper which optionally obeys a field tag and -// a tag name mapper func given by f. For each field, the mapped name will be f(tagName, extraParam ) -// if function f is not nil. -func NewMapperTagNameFunc(tagName string, f func(string, string) string) *Mapper { +// NewMapperTagFunc returns a new mapper which contains a mapper for field names +// AND a mapper for tag values. This is useful for tags like json which can +// have values like "name,omitempty". +func NewMapperTagFunc(tagName string, mapFunc, tagMapFunc func(string) string) *Mapper { return &Mapper{ - cache: make(map[reflect.Type]fieldMap), - tagName: tagName, - tagNameMapFunc: f, + cache: make(map[reflect.Type]fieldMap), + tagName: tagName, + mapFunc: mapFunc, + tagMapFunc: tagMapFunc, } } @@ -63,7 +64,7 @@ func (m *Mapper) TypeMap(t reflect.Type) fieldMap { m.mutex.Lock() mapping, ok := m.cache[t] if !ok { - mapping = getMapping(t, m.tagName, m.tagNameMapFunc, m.mapFunc) + mapping = getMapping(t, m.tagName, m.mapFunc, m.tagMapFunc) m.cache[t] = mapping } m.mutex.Unlock() @@ -214,9 +215,9 @@ func apnd(is []int, i int) []int { return x } -// getMapping returns a mapping for the t type, using the tagName, tagNameMapFunc and the mapFunc -// to determine the canonical names of fields. -func getMapping(t reflect.Type, tagName string, tagNameMapFunc func(string, string) string, mapFunc func(string) string) fieldMap { +// getMapping returns a mapping for the t type, using the tagName, mapFunc and +// tagMapFunc to determine the canonical names of fields. +func getMapping(t reflect.Type, tagName string, mapFunc, tagMapFunc func(string) string) fieldMap { queue := []typeQueue{} queue = append(queue, typeQueue{Deref(t), []int{}}) m := fieldMap{} @@ -235,8 +236,8 @@ func getMapping(t reflect.Type, tagName string, tagNameMapFunc func(string, stri } else { name = f.Name } - } else if tagNameMapFunc != nil { - name = tagNameMapFunc(tagName, name) + } else if tagMapFunc != nil { + name = tagMapFunc(name) } // if the name is "-", disabled via a tag, skip it diff --git a/reflectx/reflect_test.go b/reflectx/reflect_test.go index e8068ac1..1fcba66c 100644 --- a/reflectx/reflect_test.go +++ b/reflectx/reflect_test.go @@ -74,7 +74,7 @@ func TestTagNameMapping(t *testing.T) { StrategyName string } - m := NewMapperTagNameFunc("json", func(tagName string, value string) string { + m := NewMapperTagFunc("json", strings.ToUpper, func(value string) string { if strings.Contains(value, ",") { return strings.Split(value, ",")[0] } else { @@ -84,7 +84,7 @@ func TestTagNameMapping(t *testing.T) { strategy := Strategy{"1", "Alpah"} mapping := m.TypeMap(reflect.TypeOf(strategy)) - for _, key := range []string{"strategy_id", "StrategyName"} { + for _, key := range []string{"strategy_id", "STRATEGYNAME"} { if _, ok := mapping[key]; !ok { t.Errorf("Expecting to find key %s in mapping but did not.", key) }