Skip to content

Commit

Permalink
Merge pull request jmoiron#118 from jmoiron/liaoliaopro-master
Browse files Browse the repository at this point in the history
tag handling for reflectx
  • Loading branch information
jmoiron committed Jan 10, 2015
2 parents b468c08 + 621e1ee commit 69738bd
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 8 deletions.
31 changes: 23 additions & 8 deletions reflectx/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
tagMapFunc func(string) string
mapFunc func(string) string
mutex sync.Mutex
}

// NewMapper returns a new mapper which optionally obeys the field tag given
Expand All @@ -34,6 +35,18 @@ func NewMapper(tagName 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,
mapFunc: mapFunc,
tagMapFunc: tagMapFunc,
}
}

// 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)
Expand All @@ -51,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.mapFunc)
mapping = getMapping(t, m.tagName, m.mapFunc, m.tagMapFunc)
m.cache[t] = mapping
}
m.mutex.Unlock()
Expand Down Expand Up @@ -202,9 +215,9 @@ func apnd(is []int, i int) []int {
return x
}

// 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 {
// 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{}
Expand All @@ -223,6 +236,8 @@ func getMapping(t reflect.Type, tagName string, mapFunc func(string) string) fie
} else {
name = f.Name
}
} else if tagMapFunc != nil {
name = tagMapFunc(name)
}

// if the name is "-", disabled via a tag, skip it
Expand Down
23 changes: 23 additions & 0 deletions reflectx/reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 := NewMapperTagFunc("json", strings.ToUpper, func(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
Expand Down

0 comments on commit 69738bd

Please sign in to comment.