我有一个Set函数,它在我自己的结构中包含一个名为sessions的用户对象(或变量)。它将它分配给我的sessions结构的Value字段。 Set函数然后编组此结构并将字符串分配给存储中的某个位置。
我的问题是我不确定如何实现我的Get函数只返回存储在Value字段中的unmarshalled结构,而不是整个会话包装结构。
我做了一个非常简单的example来展示我正在谈论的内容。
我无法在我的Get func中使用类型断言,因为我不知道用户将提前使用哪种类型。
我怀疑使用反射可能有办法实现这一目标吗?
编辑:到目前为止,我们提供的两个答案并不是我想要的。我不知道用户将使用什么类型,它可能是任何类型,所以通过硬编码他们的类型或尝试"猜测"来编码。它可能含有什么不起作用。
答案 0 :(得分:1)
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"reflect"
)
type session struct {
Value interface{}
Flash map[string]string
}
type Person struct {
Name string
Age int
}
func Get(pointer interface{}) {
marshalledString := `{"Value":{"Name":"bob","Age":3},"Flash":null}`
var sess session
d := json.NewDecoder(bytes.NewBuffer([]byte(marshalledString)))
d.UseNumber()
if err := d.Decode(&sess); err != nil {
panic(err)
}
fmt.Printf("%#v", sess)
switch sess.Value.(type) {
case map[string]interface{}:
err := FillStruct(sess.Value.(map[string]interface{}), pointer)
if err != nil {
log.Fatal(err)
}
default:
return // You may want to return an error here...
}
}
func main() {
var personObj Person
Get(&personObj)
// Wanting to see personObj here have Name "bob" and Age 3
fmt.Printf("%#v", personObj)
}
func SetField(obj interface{}, name string, value interface{}) error {
structValue := reflect.ValueOf(obj).Elem()
structFieldValue := structValue.FieldByName(name)
if !structFieldValue.IsValid() {
return fmt.Errorf("No such field: %s in obj", name)
}
if !structFieldValue.CanSet() {
return fmt.Errorf("Cannot set %s field value", name)
}
structFieldType := structFieldValue.Type()
val := reflect.ValueOf(value)
if _, ok := value.(json.Number); ok {
if f, err := value.(json.Number).Int64(); err == nil {
structFieldValue.SetInt(f)
return nil
}
if f, err := value.(json.Number).Float64(); err == nil {
structFieldValue.SetFloat(f)
return nil
}
}
if structFieldType != val.Type() {
return errors.New(fmt.Sprintf("Provided value type [%s] didn't match obj field type [%s]", val.Type().String(), structFieldType.String()))
}
structFieldValue.Set(val)
return nil
}
func FillStruct(m map[string]interface{}, s interface{}) error {
for k, v := range m {
err := SetField(s, k, v)
if err != nil {
return err
}
}
return nil
}
答案 1 :(得分:0)
用户可以传入任何值,但是您的代码可以通过将错误传回给他们来处理无效输入。如果您知道所需的传入数据格式,则可以直接解组并单独处理任何无效输入。这消除了使用难以处理的中间接口{}的需要:
https://play.golang.org/p/VNCflbk3GL
package main
import (
"encoding/json"
"fmt"
)
type session struct {
Value Person
Flash map[string]string
}
type Person struct {
Name string
Age int
}
func Get(marshaled string) (Person, error) {
var sess session
err := json.Unmarshal([]byte(marshaled), &sess)
if err != nil {
return Person{}, err
}
fmt.Println(sess) // {{bob 3} map[]}
return sess.Value, nil
}
func main() {
person, err := Get(`{"Value":{"Name":"bob","Age":3},"Flash":null}`)
if err != nil {
fmt.Println("Got err:", err)
}
fmt.Printf("%#v", person) // main.Person{Name:"bob", Age:3}
}
如果Value对多种类型有效,那么你必须在某处做一个类型断言。在Go中,并不是那么痛苦:
https://newfivefour.com/golang-interface-type-assertions-switch.html
switch v := anything.(type) {
case string:
fmt.Println(v)
case int32, int64:
fmt.Println(v)
case SomeCustomType:
fmt.Println(v)
default:
fmt.Println("unknown")
}
答案 2 :(得分:0)
你的问题是你的传入数据类型的值是map [string] interface {},并且在Go中没有直接/本地方式将地图转换为你的类型(虽然那里肯定有代码)。
行。如果我们假设我们完全无法控制Value字段中的传入数据,但是,我们仍然可以通过其属性的组合来识别数据类型,对吧?因为根据定义,您应该知道可能的选项。我们可以创建一个通用的传入对象而不是接口{}。 AWS在其Go SDK中使用了类似的方法,至少对于DynamoDB服务,通过指针设置可选属性:https://github.com/aws/aws-sdk-go/blob/master/service/dynamodb/examples_test.go#L32
所以,方法是:你的UnknownObj结构将具有可以在json.Unmarshal上填充(也可能不是)的可选属性。知道通过交换机传送了哪些字段,您可以猜测发送的数据。
package main
import (
"encoding/json"
"fmt"
)
type session struct {
Value UnknownObj
Flash map[string]string
}
type UnknownObj struct {
Name *string
Age *float64
SomeOtherField *map[string]string
}
func Get() UnknownObj {
marshalledString := `{"Value":{"Name":"bob","Age":3},"Flash":null}`
var sess session
json.Unmarshal([]byte(marshalledString), &sess)
return sess.Value
}
func main() {
v := Get()
switch {
case v.Name != nil && v.Age != nil:
fmt.Println("This is a Person")
default:
fmt.Println("Unknown data type")
}
}
但是,如果您可以控制根/值字段,并且可以请求为每个类型发送特定字段,而不是全部按下值,那么您可以:
type session struct {
Person *Person
Car *Car
Building *Buidling
Etc *Etc
...
}
这样,您的解决方案将更加轻松 - >你只需要检查什么属性不是零。
希望这有帮助。
干杯。
2016年12月15日更新 回复您对框架的评论:您所描述的是将用户请求绑定到任意数据类型的过程。 好。不幸的是,它发布的代码太多了,但是这里有一个链接作为起点: https://github.com/go-playground/validator/blob/v8.18.1/validator.go#L498
这是Gin框架用于绑定的包和方法:https://github.com/gin-gonic/gin/blob/master/binding/json.go
祝你好运!