英文:
How do I set a dynamic struct field in Go?
问题
我正在使用Go语言将JSON解组为map[string]interface{}
,并使用接口的混合字符串、浮点数和切片值来填充PlaceNode
结构体的字段值。
我需要类似于"默认值"的东西,因为并不是所有的JSON对象都具有所有的结构字段。如果从其他语言背景来看,如果结构体是可索引的,我会习惯于像在JavaScript中设置n Placenode
变量的值一样做一些操作(例如,就像在JavaScript中的self
或this
关键字一样)。
n[key] = value;
然而,在Go语言中,我有一个在PlaceNode
结构体上的方法来读取interface{}
,使用reflect
并在字段可以设置时可选地分配一个值。我的interfaces{}
并不实现所有的值,所以我在直接解组到结构体时遇到了问题。
显然,没有一个字段通过了这个s.CanSet()
检查。所以我一定是做错了。
在Go语言中如何设置动态结构体字段?
func (n PlaceNode) New(data map[string]interface{}) PlaceNode {
for key, val := range data {
n.Id = key
for k, v := range val.(map[string]interface{}) {
f := reflect.ValueOf(v)
st := reflect.ValueOf(n)
if st.Kind() == reflect.Struct {
s := st.FieldByName(k)
if f.Kind() == reflect.String && true == s.CanSet() {
s.SetString(f.String())
} else if f.Kind() == reflect.Float64 && true == s.CanSet() {
s.SetFloat(f.Float())
} else if f.Kind() == reflect.Slice && true == s.CanSet() {
s.Set(f.Slice(0, f.Len()))
}
}
}
}
return n
}
data
参数map[string]interface{}
的接口也是一个map[string]interface{}
,看起来像这样:
{
"XV.12A": {
"Area": 1189.132667,
"CensusBlock": 2032,
"CensusBlockGroup": 2,
"CensusCbsaFips": 40900,
"CensusCountyFips": 61,
"CensusMcdFips": 90160,
"CensusMsaFips": 6922,
"CensusPlaceFips": 3204,
"CensusStateFips": 6,
"CensusTract": 203,
"CensusYear": 2010,
"Radius": 19.455402434548,
"RegionSize": 1189.132667
}
}
英文:
I'm unmarshalling JSON in Go to a map[string]interface{}
and using the interface's mixed string, float and slice values to populate field values of a PlaceNode
struct.
I need something like "Default values" because not all JSON objects have all Struct fields. Coming from other language backgrounds, were Structs indexable I'd be used to doing something like this to set a value on the n Placenode
variable (e.g. as if it were a self
or this
keyword in JavaScript).
n[key] = value;
Instead, I have a method on my PlaceNode
struct to read the interface{}
, use reflect
and optionally assign a value if the field can be set. My interfaces{}
don't implement all values and so I'm having trouble unmarshmaling directly into my struct.
Apparently none of the fields pass this s.CanSet()
check. So I must be going about this wrong.
How do I set a dynamic struct field in Go?
func (n PlaceNode) New(data map[string]interface{}) PlaceNode {
for key, val := range data {
n.Id = key
for k, v := range val.(map[string]interface{}) {
f := reflect.ValueOf(v)
st := reflect.ValueOf(n)
if (st.Kind() == reflect.Struct) {
s := st.FieldByName(k)
if f.Kind() == reflect.String && true == s.CanSet() {
s.SetString(f.String());
} else if f.Kind() == reflect.Float64 && true == s.CanSet() {
s.SetFloat(f.Float());
} else if f.Kind() == reflect.Slice && true == s.CanSet() {
s.Set(f.Slice(0, f.Len()));
}
}
}
}
return n
}
The data
argument map[string]interface{}
has an interface that is also a map[string]interface{}
and looks like this:
{
"XV.12A": {
"Area": 1189.132667,
"CensusBlock": 2032,
"CensusBlockGroup": 2,
"CensusCbsaFips": 40900,
"CensusCountyFips": 61,
"CensusMcdFips": 90160,
"CensusMsaFips": 6922,
"CensusPlaceFips": 3204,
"CensusStateFips": 6,
"CensusTract": 203,
"CensusYear": 2010,
"Radius": 19.455402434548,
"RegionSize": 1189.132667
}
}
答案1
得分: 16
当你进行以下调用时:
st := reflect.ValueOf(n)
ValueOf
接收到的是 PlaceNode
结构体的一个副本。因此,对 st
所做的任何更改都不会在 n
中看到。因此,该包将此类情况视为不可寻址的值。如果你想要一个代表 n
的 reflect.Value
,可以尝试使用类似以下的代码:
st := reflect.ValueOf(&n).Elem()
现在,st
直接与 n
一起工作,而不是一个副本,并且是可寻址的。现在,你应该能够在 st
及其字段上使用各种 Set*
方法。
英文:
When you make the following call:
st := reflect.ValueOf(n)
ValueOf
is passed a copy of the PlaceNode
struct. So any changes made to st
would not be seen in n
. For this reason, the package treats cases like this as non-addressable values. If you want a reflect.Value
that represernts n
, try using something like this:
st := reflect.ValueOf(&n).Elem()
Now st
is working directly with n
rather than a copy, and is addressable. You should now be able to use the various Set*
methods on it and its fields.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论