I am facing a issue with update struct fields using golang
As the var name suggest some current config fields should be updated with the requested config
currentConfig:=`
{
"field_one": "value",
"data": {
"field_two": [
"data1",
"data2"
],
"field_three": "check",
"field_four": 12
},
"field_five": [
"data1",
"data2"
],
"data2": {
"field_six":{
"field_seven": 100
}
}
}`
updateRequest:=`
{
"data": {
"field_three": "updated check" //ignore if same value exist (field_three exists in current config)
},
"field_five": ["data3"], // append to current config
"data2": {
"field_six":{
"field_eight": 300 // add value if doesnt exist to current
}
}
}`
func main() {
config := make(map[string]interface{})
err := json.Unmarshal([]byte(currentConfig), &config)
if err != nil {
panic(err)
}
updateFields := make(map[string]interface{})
err1 := json.Unmarshal([]byte(updateRequest), &updateFields)
if err1 != nil {
panic(err1)
}
fmt.Println(config)
updateFields = ParseJsonMap(updateFields, config)
fmt.Println(updateFields)
}
func ParseJsonMap(aMap map[string]interface{}, finalMap map[string]interface{}) map[string]interface{} {
parseMap("", aMap, &finalMap)
return finalMap
}
Traverses the struct and updates the fields
func parseMap(k string, aMap map[string]interface{}, finalMap *map[string]interface{}) {
if len(aMap) == 0 {
(*finalMap)[k] = nil
return
}
for key, val := range aMap {
if val != nil {
switch concreteVal := val.(type) {
case map[string]interface{}:
if _, ok := (*finalMap)[getKey(k, key)]; ok {
parseMap(getKey(k, key), val.(map[string]interface{}), finalMap)
} else {
(*finalMap)[getKey(k, key)] = val
}
case []interface{}:
res := val.([]interface{})
if arr, ok := (*finalMap)[getKey(k, key)]; ok {
for _, valueIn := range res {
arr = append(arr.([]interface{}), valueIn)
}
(*finalMap)[getKey(k, key)] = arr
} else {
(*finalMap)[getKey(k, key)] = res
}
default:
concreteValType := reflect.TypeOf(concreteVal)
if concreteValType.Kind() == reflect.Map {
parseMap(getKey(k, key), concreteVal.(map[string]interface{}), finalMap)
} else {
if _, ok := (*finalMap)[getKey(k, key)]; !ok {
(*finalMap)[getKey(k, key)] = concreteVal
}
}
}
} else {
(*finalMap)[getKey(k, key)] = nil
}
}
}
func getKey(k string, key string) string {
if k == "" {
return key
}
return k + "." + key
}
Expected Result
map[data:map[field_four:12 field_three:check field_two:[data1 data2]] data2:map[field_six:map[field_eight:300 field_seven:100]] field_five:[data1 data2 data3] field_one:value]
{
"field_one": "value",
"data": {
"field_two": [
"data1",
"data2"
],
"field_three": "check", //since key exist with data not updated
"field_four": 12
},
"field_five": [
"data1",
"data2",
"data3" //data 3 appended
],
"data2": {
"field_six":{
"field_seven": 100,
"field_eight": 300 //field is added
}
}
}
Result got - created key at top level
map[data:map[field_four:12 field_three:check field_two:[data1 data2]] data.field_three:check changed data2:map[field_six:map[field_seven:100]] data2.field_six:map[field_eight:300] field_five:[data1 data2 data3] field_one:value]
Just want to know if this is supported, if yes can you help me with it and if any better approaches exist
.used ingetKeysbut json spec does not. If you want to changefield_threeunderdataor whatever, you need to do it within that map, not within the parent map with a dot in the key. That's JSON's rule.structs, but in this casestructs don't help much, since the keys are not known. Usinginterface{},[]interface{}, andmap[string]interface{}with type checks makes sense to me