Golang,将嵌入的结构体转换为数组。

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

Golang, convert embedded struct to array

问题

在Golang中,将结构体转换为值数组有一种简单且快速的方法。你可以使用反射来实现这个功能。下面是一个示例代码:

import (
    "reflect"
)

func structToArray(s interface{}) []interface{} {
    v := reflect.ValueOf(s)
    if v.Kind() != reflect.Struct {
        return nil
    }

    var values []interface{}
    for i := 0; i < v.NumField(); i++ {
        field := v.Field(i)
        values = append(values, field.Interface())
    }

    return values
}

你可以将你的结构体传递给structToArray函数,它将返回一个包含结构体字段值的数组。如果结构体中有嵌套的结构体,它们也会被转换为数组。

你可以在你的代码中使用这个函数,例如:

user := User{}
// 初始化user的字段...

array := structToArray(user)
// 使用转换后的数组...

希望这可以帮助到你!如果你有任何其他问题,请随时问我。

英文:

Is there a way to convert struct to array of values in Golang?

for example if I have this kind of struct (not just this one):

type Model struct {
    Id        bson.ObjectId   `bson:&quot;_id,omitempty&quot;`
	CreatedAt time.Time       `bson:&quot;,omitempty&quot;`
	UpdatedAt time.Time       `bson:&quot;,omitempty&quot;`
	DeletedAt time.Time       `bson:&quot;,omitempty&quot;`
	CreatedBy bson.ObjectId   `bson:&quot;,omitempty&quot;`
	UpdatedBy bson.ObjectId   `bson:&quot;,omitempty&quot;`
	DeletedBy bson.ObjectId   `bson:&quot;,omitempty&quot;`
	Logs      []bson.ObjectId `bson:&quot;,omitempty&quot;`
}

type User struct {
	Name  string `bson:&quot;name&quot;`
	Model `bson:&quot;,inline&quot;`
}

The case was, I usually send the JSON to the browser with this format:

var iota = -1
var data = {
  NAME: ++iota, ID: ++iota, CREATED_AT: ++iota, UPDATED_AT: ++iota, DELETED_AT: ++iota, // and so on
  rows: [[&#39;kiz&#39;,1,&#39;2014-01-01&#39;,&#39;2014-01-01&#39;,&#39;2014-01-01&#39;],
         [&#39;yui&#39;,2,&#39;2014-01-01&#39;,&#39;2014-01-01&#39;,&#39;2014-01-01&#39;],
         [&#39;ham&#39;,3,&#39;2014-01-01&#39;,&#39;2014-01-01&#39;,&#39;2014-01-01&#39;] // and so on
        ]
};

Instead of:

var data = {
  rows: [{NAME:&#39;kiz&#39;,ID:1,CreatedAt:&#39;2014-01-01&#39;,UpdatedAt:&#39;2014-01-01&#39;,DeletedAt:&#39;2014-01-01&#39;},
         {NAME:&#39;yui&#39;,ID:2,CreatedAt:&#39;2014-01-01&#39;,UpdatedAt:&#39;2014-01-01&#39;,DeletedAt:&#39;2014-01-01&#39;},
         {NAME:&#39;ham&#39;,ID:3,CreatedAt:&#39;2014-01-01&#39;,UpdatedAt:&#39;2014-01-01&#39;,DeletedAt:&#39;2014-01-01&#39;} // and so on
        ]
}

Here's what I've tried:

import (
	&quot;github.com/kr/pretty&quot;
	//&quot;gopkg.in/mgo.v2&quot;
	&quot;gopkg.in/mgo.v2/bson&quot;
	&quot;reflect&quot;
	&quot;runtime&quot;
	&quot;strings&quot;
	&quot;time&quot;
)

// copy the model from above

func Explain(variable interface{}) {
	_, file, line, _ := runtime.Caller(1)
	//res, _ := json.MarshalIndent(variable, &quot;   &quot;, &quot;  &quot;)
	res := pretty.Formatter(variable)
	fmt.Printf(&quot;%s:%d: %# v\n&quot;, file[len(FILE_PATH):], line, res)
	//spew.Dump(variable)
}

func s2a(i interface{}) []interface{} { // taken from https://gist.github.com/tonyhb/5819315
	iVal := reflect.ValueOf(i).Elem()
	//typ := iVal.Type()
	values := make([]interface{}, 0, iVal.NumField())
	for i := 0; i &lt; iVal.NumField(); i++ {
		f := iVal.Field(i)
		//tag := typ.Field(i).Tag.Get(&quot;tagname&quot;)
		//fmt.Println(tag)
		// name := typ.Field(i).Name
		v := f.Interface()
		switch v.(type) {
		case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, string, []byte, time.Time:
			// do nothing
		// case struct{}: // how to catch any embeeded struct?
		case Model: // Model (or any embedded/nameless struct) should also converted to array
			//arr := s2a() // invalid type assertion: f.(Model) (non-interface type reflect.Value on left)
			//arr := s2a(f.Addr().(&amp;Model)) // invalid type assertion: f.Addr().(&amp;Model) (non-interface type reflect.Value on left)
			// umm.. how to convert f back to Model?
			//for _, e := range arr {
				values = append(values, e)
			//}
		default: // struct? but also interface and map T_T
			//v = s2a(&amp;v)
		}
		values = append(values, v)
	}
	return values
}

func main() {
	//sess, err := mgo.Dial(&quot;127.0.0.1&quot;)
	//Check(err, &quot;unable to connect&quot;)
	//db := sess.DB(&quot;test&quot;)
	//coll := db.C(&quot;coll1&quot;)
	user := User{}
	user.Id = bson.NewObjectId()
	user.Name = &quot;kis&quot;
	//changeInfo, err := coll.UpsertId(user.Id, user)
	//Check(err, &quot;failed to insert&quot;)
	//Explain(changeInfo)
	//Explain(s2a(changeInfo))
	user.Name = &quot;test&quot;
	Explain(user)
	Explain(s2a(&amp;user))
	//err = coll.FindId(user.Id).One(&amp;user)
	//Check(err, &quot;failed to fetch&quot;)
	//Explain(user)
	//Explain(s2a(&amp;user))
	user.CreatedAt = time.Now()
	//err = coll.UpdateId(user.Id, user)
	//Check(err, &quot;failed to update&quot;)
	Explain(changeInfo)
	Explain(s2a(&amp;user))
	user.CreatedAt = user.DeletedAt
	//err = coll.FindId(user.Id).One(&amp;user)
	//Check(err, &quot;failed to fetch&quot;)
	Explain(user)
	Explain(s2a(&amp;user))
}

Is there easy/fast way to convert struct to array (and if there struct embedded/inside it, converted to array also)?

答案1

得分: 3

如果你希望为数组表示中的字段指定固定顺序,你可以通过实现json.Marshaler接口来自定义其表示方式。例如:

func (u User) MarshalJSON() ([]byte, error) {
    a := []interface{}{
        u.Name,
        u.Id,
        ...,
    }
    return json.Marshal(a)
}

现在,当你对这种类型的变量进行编组时,它们将被表示为一个数组。如果你还想实现反向操作(将数组解组为该结构体),你还需要实现json.Unmarshaler接口。可以使用类似的方法,使用json.Unmarshal解码为[]interface{}切片,然后提取值。但是,请确保UnmarshalJSON声明为使用指针接收器,否则你的代码将无法正常工作(你将更新结构体的副本而不是结构体本身)。

英文:

If you are happy to specify a fixed order for the fields in the array representation, you could do this by implementing the json.Marshaler interface to customise its representation. For example:

func (u User) MarshalJSON() ([]byte, error) {
    a := []interface{}{
        u.Name,
        u.Id,
        ...,
    }
    return json.Marshal(a)
}

Now when you marshal variables of this type, they will be represented as an array. If you want to also do the reverse (unmarshal an array into this struct), you will also need to implement the json.Unmarshaler interface. This could be done in a similar fashion, using json.Unmarshal to decode into a []interface{} slice and then pull out the values. Make sure UnmarshalJSON is declared to take a pointer receiver though, or your code won't work (you'll end up updating a copy of the struct rather than the struct itself).

答案2

得分: 2

为什么不使用reflect.Kind()?这是playground链接:http://play.golang.org/p/YjbsnB4eln

英文:

Why not use reflect.Kind()? Here's the playground: http://play.golang.org/p/YjbsnB4eln

答案3

得分: 2

使用reflect包。

这是一些playground代码,它可以用于一个记录(任何结构类型),你可以重构它以适用于记录的切片。

编辑:(为了保险起见复制粘贴)

package main

import "fmt"
import "strings"
import "reflect"

type X struct {
    Y string
    Z int
}

func main() {
    data := X{"yval", 3}
    expectedResult := `{"Y": 0, "Z": 1, "rows": [["yval", 3]]}`
    
    fmt.Println(convert(data))
    fmt.Println(expectedResult)
}

func convert(data interface{}) string {
    v := reflect.ValueOf(data)
    n := v.NumField()
    
    st := reflect.TypeOf(data)
    headers := make([]string, n)
    for i := 0; i < n; i++ {
        headers[i] = fmt.Sprintf(`"%s": %d`, st.Field(i).Name, i)
    }
    
    rowContents := make([]string, n)
    for i := 0; i < n; i++ {
        x := v.Field(i)
        s := fmt.Sprintf("%v", x.Interface())
        if x.Type().String() == "string" {
            s = `"` + s + `"`
        }
        rowContents[i] = s
    }
    
    return "{" + strings.Join(headers, ", ") + `, "rows": [[` + strings.Join(rowContents, ", ") + "]]}"
}
英文:

Use the reflect package.

Here's some playground code that'll work for one record (of any struct type), you can refactor it to work for a slice of records.

EDIT: (copy-pasted for good measure)

package main

import &quot;fmt&quot;
import &quot;strings&quot;
import &quot;reflect&quot;

type X struct {
    Y string
    Z int
}

func main() {
    data := X{&quot;yval&quot;,3}
    expectedResult := `{&quot;Y&quot;: 0, &quot;Z&quot;: 1, &quot;rows&quot;: [[&quot;yval&quot;, 3]]}`
    
    fmt.Println(convert(data))
    fmt.Println(expectedResult)
}

func convert(data interface{}) string {
    v := reflect.ValueOf(data)
    n := v.NumField()
    
    st := reflect.TypeOf(data)
    headers := make([]string, n)
    for i := 0; i &lt; n; i++ {
        headers[i] = fmt.Sprintf(`&quot;%s&quot;: %d`, st.Field(i).Name, i)
    }
    
    rowContents := make([]string, n)
    for i := 0; i &lt; n; i++ {
        x := v.Field(i)
        s := fmt.Sprintf(&quot;%v&quot;, x.Interface())
        if x.Type().String() == &quot;string&quot; {
            s = `&quot;` + s + `&quot;`
        }
        rowContents[i] = s
    }
    
    return &quot;{&quot; + strings.Join(headers, &quot;, &quot;) + `, &quot;rows&quot;: [[` + strings.Join(rowContents, &quot;, &quot;) + &quot;]]}&quot;
}

huangapple
  • 本文由 发表于 2014年11月26日 11:54:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/27141037.html
匿名

发表评论

匿名网友

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

确定