在 Golang 中,可以使用递归和过滤来对对象的字符串值进行补丁操作。

huangapple go评论81阅读模式
英文:

golang patch string values on an object, recursive with filtering

问题

社区,

任务

基本

实现一个func,用于修补objects上的所有string字段。

详细说明

  • [已完成] 只有当字段与matcher func匹配时,才应进行修补。
  • [已完成] 值应通过process func进行处理。
  • 修补应递归进行。
  • 它还应适用于[]string[]*string,以及对structs[]struct[]*struct进行递归修补。

// 更新 - 删除了旧代码

英文:

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

答案1

得分: 0

解决方案

结构体

更新了结构体的使用(尽管这不会影响实际程序,我使用这个来完整性)

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"`
}

辅助函数

此外,我们需要两个辅助函数来检查一个struct是否具有一个tag,以及向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 - 实际解决方案

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)
}

如何使用

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

英文:

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:&quot;yes,TagName&quot;`
	NamePtr *string `process:&quot;no,TagNamePtr&quot;`
}

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

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, &quot;&quot;, &quot; &quot;)
	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, &quot;,&quot;)
		if len(parts) &gt; 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 &lt; v.NumField(); i++ {
			var sf = v.Type().Field(i)
			structFiled = &amp;sf
			p.value(i, v.Field(i), structFiled)
		}
	case reflect.Slice:
		for i := 0; i &lt; 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 = &quot;golang&quot;
	var SubNamePtr string = &quot;*secure&quot;
	testUser := User{
		ID:      1,
		Name:    &quot;lumo&quot;,
		NamePtr: &amp;NamePtr,
		SubStruct: []Tag{{
			Name: &quot;go&quot;,
		},
		},
		SubStructPtr: []*Tag{&amp;Tag{
			Name:    &quot;*go&quot;,
			NamePtr: &amp;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, &quot;process&quot;, &quot;yes&quot;)
		},
		// process
		Process: func(in string) string {
			if in != &quot;&quot; {
				return fmt.Sprintf(&quot;!%s!&quot;, strings.ToUpper(in))
			} else {
				return &quot;!empty!&quot;
			}
			return in
		},
	}

	p.Apply(&amp;testUser)
	fmt.Println(&quot;Output:&quot;)
	fmt.Println(Stringify(testUser))
}

goplay

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

huangapple
  • 本文由 发表于 2022年1月24日 23:22:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/70836213.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定