How do I set a dynamic structure field in Go?
I am parsing JSON in Go in map[string]interface{}
and using the mixed string, float and slice interface values to populate the structure field values PlaceNode
.
I need something like "default values" because not all JSON objects have all Struct fields. Coming from other language backgrounds, there were indexed Struct that I would use to do something like this to set a value in a variable n Placenode
(for example, as if it were a keyword self
or this
in JavaScript).
n[key] = value;
Instead, I have a method in my structure PlaceNode
to read interface{}
, use reflect
and optionally assign a value if the field can be set. Mine interfaces{}
does not implement all values and therefore I am having problems with unmarshmaling directly in my framework.
Apparently, none of the fields are validating s.CanSet()
. So I have to go about it wrong.
How do I set a dynamic structure 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 argument data
map[string]interface{}
has an interface that is also 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
}
}
source to share
When making the next call:
st := reflect.ValueOf(n)
ValueOf
a copy of the structure is passed PlaceNode
. Therefore, any changes made to st
will not show up in n
. For this reason, the package treats such cases as unaddressed values. If you want reflect.Value
which represents n
, try using something like this:
st := reflect.ValueOf(&n).Elem()
Now st
works directly with n
, not a copy, and is addressed. Now you have to use different methods Set*
and its fields.
source to share