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
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With