需要帮助解决“reflect: NumField of non-struct type”错误。

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

Need help getting past 'reflect: NumField of non-struct type'

问题

我正在尝试构建一个Query结构体,用于表示与我们的Cassandra数据库之间的数据传输。

问题是,我试图将一个Type作为结构体中的一个字段,以便以后可以重建它。我觉得我离成功很近,但是出现了一些问题。我尝试这样做时,出现了一个非常严重的错误:

2015/11/17 15:42:22 http: panic serving 127.0.0.1:57962: reflect: NumField of non-struct type
goroutine 34 [running]:
net/http.(*conn).serve.func1(0xc820184000, 0x7f36d7459b00, 0xc820180008)
	/usr/lib/go/src/net/http/server.go:1287 +0xb5
reflect.(*rtype).NumField(0x790820, 0xc8200b9a60)
	/usr/lib/go/src/reflect/type.go:660 +0x7b
github.com/relops/cqlr.(*Binding).compile(0xc82004f6f0, 0x77ab60, 0xc8200b9a60, 0x16, 0xc820194140, 0x5, 0x5, 0x0, 0x0)
	/home/jared/dev/go-pp/src/github.com/relops/cqlr/cqlr.go:160 +0xf8
github.com/relops/cqlr.(*Binding).Scan(0xc82004f6f0, 0x77ab60, 0xc8200b9a60, 0x825280)
	/home/jared/dev/go-pp/src/github.com/relops/cqlr/cqlr.go:99 +0x199
main/cassandra/query.Query.RetryingQuery(0x9325e0, 0x19, 0x0, 0x0, 0x0, 0x0, 0x7f36d74580a8, 0x87b120, 0x0, 0x0, ...)
	/home/jared/dev/go-pp/src/main/cassandra/query/query.go:39 +0x39e
main.ViewHosts(0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/home/jared/dev/go-pp/src/main/handlers.go:86 +0x1f3
net/http.HandlerFunc.ServeHTTP(0x9a03b0, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/usr/lib/go/src/net/http/server.go:1422 +0x3a
main/utils.Logger.func1(0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/home/jared/dev/go-pp/src/main/utils/logger.go:32 +0x9c
net/http.HandlerFunc.ServeHTTP(0xc820109200, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/usr/lib/go/src/net/http/server.go:1422 +0x3a
github.com/gorilla/mux.(*Router).ServeHTTP(0xc82001aa00, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/home/jared/dev/go-pp/src/github.com/gorilla/mux/mux.go:100 +0x29e
net/http.serverHandler.ServeHTTP(0xc82016b1a0, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/usr/lib/go/src/net/http/server.go:1862 +0x19e
net/http.(*conn).serve(0xc820184000)
	/usr/lib/go/src/net/http/server.go:1361 +0xbee
created by net/http.(*Server).Serve
	/usr/lib/go/src/net/http/server.go:1910 +0x3f6

以下是代码:

type Query struct {
	query       string
	values		interface{}
	attempts    int
	maxAttempts int
	structType 	reflect.Type
}

func NewQuery(query string, t reflect.Type) (q Query) {
	q.query = query
	q.structType = t
	return
}

func (query Query) RetryingQuery() (results []interface{}) {
	var q *gocql.Query
	if query.values != nil {
		q = c.Session.Query(query.query, query.values)
	} else {
		q = c.Session.Query(query.query)
	}

	bindQuery := cqlr.BindQuery(q)

	value := reflect.New(query.structType).Pointer()
	for bindQuery.Scan(&value) {
		results = append(results, value)
	}
	return
}


// 在另一个文件中设置并调用查询
var host cmodels.Host
query := query.NewQuery("SELECT * FROM server.host", reflect.TypeOf(host))
queryResults := query.RetryingQuery()

然而,稍微修改一下代码,我可以避免错误,但是得到了一个奇怪的结果。

func (query Query) RetryingQuery() (results []interface{}) {
	var q *gocql.Query
	if query.values != nil {
		q = c.Session.Query(query.query, query.values)
	} else {
		q = c.Session.Query(query.query)
	}

	bindQuery := cqlr.BindQuery(q)
	value := reflect.New(query.structType)
	for bindQuery.Scan(&value) {
		results = append(results, value)
	}
	return
}

上述代码给我返回了:

[{"flag":22},{"flag":22},{"flag":22},{"flag":22},{"flag":22},...]
英文:

I am trying to build a Query struct that will represent data to and from our Cassandra database.

The issue is, I am trying to take a Type as one of my fields in the struct so I can reconstruct it later. I feel like I am really close, but it is giving me some issues. I get a really nasty looking error trying to do this:

2015/11/17 15:42:22 http: panic serving 127.0.0.1:57962: reflect: NumField of non-struct type
goroutine 34 [running]:
net/http.(*conn).serve.func1(0xc820184000, 0x7f36d7459b00, 0xc820180008)
	/usr/lib/go/src/net/http/server.go:1287 +0xb5
reflect.(*rtype).NumField(0x790820, 0xc8200b9a60)
	/usr/lib/go/src/reflect/type.go:660 +0x7b
github.com/relops/cqlr.(*Binding).compile(0xc82004f6f0, 0x77ab60, 0xc8200b9a60, 0x16, 0xc820194140, 0x5, 0x5, 0x0, 0x0)
	/home/jared/dev/go-pp/src/github.com/relops/cqlr/cqlr.go:160 +0xf8
github.com/relops/cqlr.(*Binding).Scan(0xc82004f6f0, 0x77ab60, 0xc8200b9a60, 0x825280)
	/home/jared/dev/go-pp/src/github.com/relops/cqlr/cqlr.go:99 +0x199
main/cassandra/query.Query.RetryingQuery(0x9325e0, 0x19, 0x0, 0x0, 0x0, 0x0, 0x7f36d74580a8, 0x87b120, 0x0, 0x0, ...)
	/home/jared/dev/go-pp/src/main/cassandra/query/query.go:39 +0x39e
main.ViewHosts(0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/home/jared/dev/go-pp/src/main/handlers.go:86 +0x1f3
net/http.HandlerFunc.ServeHTTP(0x9a03b0, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/usr/lib/go/src/net/http/server.go:1422 +0x3a
main/utils.Logger.func1(0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/home/jared/dev/go-pp/src/main/utils/logger.go:32 +0x9c
net/http.HandlerFunc.ServeHTTP(0xc820109200, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/usr/lib/go/src/net/http/server.go:1422 +0x3a
github.com/gorilla/mux.(*Router).ServeHTTP(0xc82001aa00, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/home/jared/dev/go-pp/src/github.com/gorilla/mux/mux.go:100 +0x29e
net/http.serverHandler.ServeHTTP(0xc82016b1a0, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
	/usr/lib/go/src/net/http/server.go:1862 +0x19e
net/http.(*conn).serve(0xc820184000)
	/usr/lib/go/src/net/http/server.go:1361 +0xbee
created by net/http.(*Server).Serve
	/usr/lib/go/src/net/http/server.go:1910 +0x3f6

Here is the code

type Query struct {
	query       string
	values		interface{}
	attempts    int
	maxAttempts int
	structType 	reflect.Type
}

func NewQuery(query string, t reflect.Type) (q Query) {
	q.query = query
	q.structType = t
	return
}

func (query Query) RetryingQuery() (results []interface{}) {
	var q *gocql.Query
	if query.values != nil {
		q = c.Session.Query(query.query, query.values)
	} else {
		q = c.Session.Query(query.query)
	}

	bindQuery := cqlr.BindQuery(q)

	value := reflect.New(query.structType).Pointer()
	for bindQuery.Scan(&value) {
		results = append(results, value)
	}
	return
}


// setting up and calling the query here (in another file)
var host cmodels.Host
query := query.NewQuery("SELECT * FROM server.host", reflect.TypeOf(host))
queryResults := query.RetryingQuery()

However, with slight modification of the code I get past the error but I am getting a weird result.

func (query Query) RetryingQuery() (results []interface{}) {
	var q *gocql.Query
	if query.values != nil {
		q = c.Session.Query(query.query, query.values)
	} else {
		q = c.Session.Query(query.query)
	}

	bindQuery := cqlr.BindQuery(q)
	value := reflect.New(query.structType)
	for bindQuery.Scan(&value) {
		results = append(results, value)
	}
	return
}

That code above gives me:

[{"flag":22},{"flag":22},{"flag":22},{"flag":22},{"flag":22},...]

答案1

得分: 6

将代码中的一段内容翻译为中文如下:

将以下代码段进行更改:

value := reflect.New(query.structType)
for bindQuery.Scan(&value) {

更改为:

value := reflect.New(query.structType).Interface()
for bindQuery.Scan(value) {

完整的工作示例代码如下所示(粘贴在这里):

package main

import "reflect"

func Scan(d interface{}) {
	v := reflect.ValueOf(d)
	i := reflect.Indirect(v)
	s := i.Type()
	println(s.NumField()) // 如果将 Host 改为具有 1 个字段,则打印出 1,否则打印出 0
}

func query(t reflect.Type) {
	value := reflect.New(t).Interface()
	Scan(value)
}

type Host struct{}
// type Host struct{int} // 注释掉上面一行,取消注释这一行,上面的 println 将打印出 1

func main() {
	var h Host
	query(reflect.TypeOf(h))
}

这段代码模拟了你的代码以及 clqr 库的功能(参见 https://github.com/relops/cqlr/blob/master/cqlr.go#L85-L99https://github.com/relops/cqlr/blob/master/cqlr.go#L154-L160)。你需要将 s := i.Type() 设置为你的 Host 结构体的 TypeOf,因此如果你从 clqr 代码中逆推,你可以推断出需要传递给 Scan 调用的正确对象类型。鉴于你的输入是 reflect.Type,你可以推断出如何从该 Type 获取到正确类型的对象以传递给 Scan

英文:

Change from:

value := reflect.New(query.structType)
for bindQuery.Scan(&value) {

to:

value := reflect.New(query.structType).Interface()
for bindQuery.Scan(value) {

See here for a full working example (pasted below):

package main

import "reflect"

func Scan(d interface{}) {
    v := reflect.ValueOf(d)
	i := reflect.Indirect(v)
    s := i.Type()
	println(s.NumField()) // will print out 0, if you change Host to have 1 field, it prints out 1
}

func query(t reflect.Type) {
    value := reflect.New(t).Interface()
	Scan(value)
}

type Host struct{}
// type Host struct{int} // comment above line, uncomment this one, and println above will print 1

func main() {
    var h Host
	query(reflect.TypeOf(h))
}

This emulates what your code, plus the clqr library, does (see https://github.com/relops/cqlr/blob/master/cqlr.go#L85-L99 and https://github.com/relops/cqlr/blob/master/cqlr.go#L154-L160). You basically need s := i.Type() to be the TypeOf your Host struct, so if you work backwards from what the clqr code is doing, you can deduce what you need to pass in to the Scan call. And given that the input you have is a reflect.Type, you can deduce how you can get from that Type to the correct kind of object to pass into Scan.

huangapple
  • 本文由 发表于 2015年11月18日 06:46:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/33768554.html
匿名

发表评论

匿名网友

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

确定