如何将结构体转换为扁平路径数组?

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

How to convert a struct into a flat array of paths?

问题

如果我有一个结构体,例如:

type Example struct {
  Foo string
  Bar string
  Baz struct{
    A int
    B string
  }
  Qux []string
}

将其转换为表示每个结构体字段的点路径的字符串切片,最佳方法是什么?

我希望输出的结果看起来像下面的切片:

["Example.Foo", "Example.Bar", "Example.Baz.A", "Example.Baz.B", "Example.Qux.0", "Example.Qux.1"]

确切的结构体在编译时是已知的。此外,从结构体到平面列表的转换在热路径上,因此性能将是一个重要的考虑因素。

任何提示将不胜感激!

英文:

If for example I have a struct such as:

type Example struct {
  Foo string
  Bar string
  Baz struct{
    A int
    B string
  }
  Qux []string
}

What would be the best approach to convert it into a flat slice of strings representing the dot path to each struct field?

I want an output that looks like the following slice:

["Example.Foo", "Example.Bar", "Example.Baz.A", "Example.Baz.B", "Example.Qux.0", "Example.Qux.1"]

The exact structs will be known at compile time. Also, the conversion from struct to a flat list is in a hot path so performance will be an important consideration.

Any hints would be appreciated!

答案1

得分: 2

你必须自己编写代码,使用反射来实现。

这是一个演示性的函数,打印出你提供的输出:

package main

import (
	"fmt"
	"reflect"
	"strconv"
)

type Example struct {
	Foo string
	Bar string
	Baz struct {
		A int
		B string
	}
	Qux []string
}

func main() {
	example := Example{Qux: []string{"a", "b"}}
	t := reflect.ValueOf(example)
	prefix := t.Type().Name()
	fmt.Println(ToPathSlice(t, prefix, make([]string, 0)))
}

func ToPathSlice(t reflect.Value, name string, dst []string) []string {
	switch t.Kind() {
	case reflect.Ptr, reflect.Interface:
		return ToPathSlice(t.Elem(), name, dst)

	case reflect.Struct:
		for i := 0; i < t.NumField(); i++ {
			fname := t.Type().Field(i).Name
			dst = ToPathSlice(t.Field(i), name+"."+fname, dst)
		}

	case reflect.Slice, reflect.Array:
		for i := 0; i < t.Len(); i++ {
			dst = ToPathSlice(t.Index(i), name+"."+strconv.Itoa(i), dst)
		}

	default:
		return append(dst, name)
	}
	return dst
}

将打印出:

[Example.Foo Example.Bar Example.Baz.A Example.Baz.B Example.Qux.0 Example.Qux.1]

请注意:

  • 反射会带来性能损失;如果你关心性能,应该对相关代码路径进行性能分析,看看是否会影响性能。
  • 上述代码是人为构造的,例如它不处理映射(maps),不处理nil等;你可以根据需要进行扩展。
  • 在你期望的输出中,会打印出切片/数组字段的索引。切片没有固定的长度,需要使用reflect.Value来获取长度。我认为这会使代码变得更加笨拙。如果你可以接受不打印切片索引,那么可以使用reflect.Type来处理。
英文:

You have to code it yourself, with reflection.

This is a demonstrative function that prints the output you provided:

package main

import (
&quot;fmt&quot;
	&quot;reflect&quot;
	&quot;strconv&quot;
)

type Example struct {
	Foo string
	Bar string
	Baz struct{
		A int
		B string
	}
	Qux []string
}

func main() {
	example := Example{Qux: []string{&quot;a&quot;, &quot;b&quot;}}
	t := reflect.ValueOf(example)
	prefix := t.Type().Name()
	fmt.Println(ToPathSlice(t, prefix, make([]string, 0)))
}

func ToPathSlice(t reflect.Value, name string, dst []string) []string {
	switch t.Kind() {
	case reflect.Ptr, reflect.Interface:
		return ToPathSlice(t.Elem(), name, dst)

	case reflect.Struct:
		for i := 0; i &lt; t.NumField(); i++ {
			fname := t.Type().Field(i).Name
			dst = ToPathSlice(t.Field(i), name+&quot;.&quot;+fname, dst)
		}

	case reflect.Slice, reflect.Array:
		for i := 0; i &lt; t.Len(); i++ {
			dst = ToPathSlice(t.Index(i), name+&quot;.&quot;+strconv.Itoa(i), dst)
		}

	default:
		return append(dst, name)
	}
	return dst
}

Will print:

[Example.Foo Example.Bar Example.Baz.A Example.Baz.B Example.Qux.0 Example.Qux.1]

Note that:

  • reflection comes at a performance penalty; if you are concerned about this, you should profile the relevant code path to see if it's a deal breaker
  • the above code is contrived, for example it doesn't handle maps, it doesn't handle nil, etc.; you can expand it yourself
  • in your desired output, the indices of slice/array fields is printed. Slices don't have inherent length as arrays. In order to know the length of slices, you have to work with reflect.Value. This IMO makes the code more awkward. If you can accept not printing indices for slices, then you can work with reflect.Type.

Playground: https://play.golang.org/p/isNFSfFiXOP

huangapple
  • 本文由 发表于 2021年11月5日 04:37:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/69845531.html
匿名

发表评论

匿名网友

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

确定