简体   繁体   English

Golang,将嵌入式结构转换为数组

[英]Golang, convert embedded struct to array

Is there a way to convert struct to array of values in Golang? 有没有一种方法可以将结构转换为Golang中的值数组?

for example if I have this kind of struct (not just this one): 例如,如果我有这种结构(不只是这种结构):

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

type User struct {
    Name  string `bson:"name"`
    Model `bson:",inline"`
}

The case was, I usually send the JSON to the browser with this format: 情况是,我通常使用以下格式将JSON发送到浏览器:

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

Instead of: 代替:

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

Here's what I've tried: 这是我尝试过的:

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

// copy the model from above

func Explain(variable interface{}) {
    _, file, line, _ := runtime.Caller(1)
    //res, _ := json.MarshalIndent(variable, "   ", "  ")
    res := pretty.Formatter(variable)
    fmt.Printf("%s:%d: %# v\n", 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 < iVal.NumField(); i++ {
        f := iVal.Field(i)
        //tag := typ.Field(i).Tag.Get("tagname")
        //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().(&Model)) // invalid type assertion: f.Addr().(&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(&v)
        }
        values = append(values, v)
    }
    return values
}

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

Is there easy/fast way to convert struct to array (and if there struct embedded/inside it, converted to array also)? 有没有简单/快速的方法可以将结构转换为数组(如果在结构中嵌入/嵌入结构,也可以转换为数组)?

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 "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, ", ") + "]]}"
}

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. 如果您愿意为数组表示形式中的字段指定固定顺序,则可以通过实现json.Marshaler接口来自定义其表示形式来实现。 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 . 如果您还想进行相反操作(将数组解组到此结构中),则还需要实现json.Unmarshaler接口 This could be done in a similar fashion, using json.Unmarshal to decode into a []interface{} slice and then pull out the values. 可以使用json.Unmarshal以类似的方式完成此操作,以json.Unmarshal其解码为[]interface{}切片,然后取出值。 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). 确保UnmarshalJSON被声明为采用指针接收器,否则您的代码将无法工作(您最终将更新结构的副本,而不是结构本身)。

Why not use reflect.Kind()? 为什么不使用reflect.Kind()? Here's the playground: http://play.golang.org/p/YjbsnB4eln 这是游乐场: http : //play.golang.org/p/YjbsnB4eln

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM