Home > Software engineering >  golang patch string values on an object, recursive with filtering
golang patch string values on an object, recursive with filtering

Time:01-28

Community,

The mission

basic

Implement a func that patches all string fields on an objects

details

  • [done] fields shall only be patched if they match a matcher func
  • [done] value shall be processed via process func
  • patching shall be done recursive
  • it shall also work for []string, []*string and recursive for structs and []struct, []*struct

// update - removed old code

CodePudding user response:

Solution

structs

updated the structs to use (though this does not affect the actual program, i use this for completeness

type Tag struct {
    Name    string  `process:"yes,TagName"`
    NamePtr *string `process:"no,TagNamePtr"`
}

type User struct {
    ID           int
    Nick         string
    Name         string    `process:"yes,UserName"`
    NamePtr      *string   `process:"yes,UserNamePtr"`
    Slice        []string  `process:"yes,Slice"`
    SlicePtr     []*string `process:"yes,SlicePtr"`
    SubStruct    []Tag     `process:"yes,SubStruct"`
    SubStructPtr []*Tag    `process:"yes,SubStructPtr"`
}

helper func

Further we need two helper funcs to check if a struct has a tag and to print to console

func Stringify(i interface{}) string {
    s, _ := json.MarshalIndent(i, "", " ")
    return string(s)
}

func HasTag(structFiled reflect.StructField, tagName string, tagValue string) bool {
    tag := structFiled.Tag
    if value, ok := tag.Lookup(tagName); ok {
        parts := strings.Split(value, ",")
        if len(parts) > 0 {
            return parts[0] == tagValue
        }
    }
    return false
}

patcher - the actual solution

type Patcher struct {
    Matcher func(structFiled *reflect.StructField, v reflect.Value) bool
    Process func(in string) string
}

func (p *Patcher) value(idx int, v reflect.Value, structFiled *reflect.StructField) {
    if !v.IsValid() {
        return
    }
    switch v.Kind() {
    case reflect.Ptr:
        p.value(idx, v.Elem(), structFiled)
    case reflect.Struct:
        for i := 0; i < v.NumField(); i   {
            var sf = v.Type().Field(i)
            structFiled = &sf
            p.value(i, v.Field(i), structFiled)
        }
    case reflect.Slice:
        for i := 0; i < v.Len(); i   {
            p.value(i, v.Index(i), structFiled)
        }
    case reflect.String:
        if p.Matcher(structFiled, v) {
            v.SetString(p.Process(v.String()))
        }
    }
}

func (p *Patcher) Apply(in interface{}) {
    p.value(-1, reflect.ValueOf(in).Elem(), nil)
}

how to use

func main() {
    var NamePtr string = "golang"
    var SubNamePtr string = "*secure"
    testUser := User{
        ID:      1,
        Name:    "lumo",
        NamePtr: &NamePtr,
        SubStruct: []Tag{{
            Name: "go",
        },
        },
        SubStructPtr: []*Tag{&Tag{
            Name:    "*go",
            NamePtr: &SubNamePtr,
        },
        },
    }

    var p = Patcher{
        // filter - return true if the field in struct has a tag process=true
        Matcher: func(structFiled *reflect.StructField, v reflect.Value) bool {
            return HasTag(*structFiled, "process", "yes")
        },
        // process
        Process: func(in string) string {
            if in != "" {
                return fmt.Sprintf("!%s!", strings.ToUpper(in))
            } else {
                return "!empty!"
            }
            return in
        },
    }

    p.Apply(&testUser)
    fmt.Println("Output:")
    fmt.Println(Stringify(testUser))
}

goplay

https://goplay.tools/snippet/-0MHDfKr7ax

  •  Tags:  
  • Related