繁体   English   中英

使用 Golang 结构更新嵌套字段

[英]Update Nested fields using Golang struct

我面临使用 golang 更新结构字段的问题

正如 var 名称所暗示的那样,一些当前的配置字段应该使用请求的配置进行更新

     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
}

遍历结构并更新字段

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
}

预期结果

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
            }
        }
     }

结果得到 - 在顶层创建密钥

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]

只是想知道这是否受支持,如果是,你能帮我吗,如果有更好的方法存在

看来您正在尝试将一个 map 叠加到另一个 map 上。如果您不想在不更改现有 map 的情况下应用此类更新,这会变得很复杂。因此,将这两个步骤分开可能更容易:

  • 复制一个map[string]interface{}
  • 将一个map[string]interface{}叠加在另一个之上

func CopyMap(m map[string]interface{}) map[string]interface{} {
    cp := make(map[string]interface{})
    for k, v := range m {
        vm, ok := v.(map[string]interface{})
        if ok {
            cp[k] = CopyMap(vm)
        } else {
            cp[k] = v
        }
    }
    return cp
}

func overlay(dst, src map[string]interface{}) error {
    for k, v := range src {

        if _, ok := dst[k]; !ok {
            dst[k] = v // easy case - dst key does not exist
            continue
        }

        d, ok1 := dst[k].(map[string]interface{})
        s, ok2 := src[k].(map[string]interface{})

        if ok1 && ok2 {
            overlay(d, s) // merge case
        } else if !ok1 && !ok2 {
            dst[k] = v // non-map - so simple assignment/reassignment
        } else {
            return fmt.Errorf("incompatible update types") // map to non-map or vice-versa
        }

    }
    return nil
}

使用:

err := json.Unmarshal([]byte(currentConfig), &config) // check err
err = json.Unmarshal([]byte(updateRequest), &updateFields) // check err
newconfig = CopyMap(config)
err = overlay(newconfig, updateFields) // check err

https://play.golang.org/p/RZPbkv19ChL

Output:

    config : map[data:map[field_four:12 field_three:check field_two:[data1 data2]] data2:map[field_six:map[field_eight:200 field_seven:100]] field_five:[data1 data2] field_one:value]
    update : map[data:map[field_three:check changed] data2:map[field_six:map[field_eight:300]] field_five:[data1]]
 newconfig : map[data:map[field_four:12 field_three:check changed field_two:[data1 data2]] data2:map[field_six:map[field_eight:300 field_seven:100]] field_five:[data1] field_one:value]

更新:处理 JSON arrays 的追加而不是替换:

func overlay2(dst, src map[string]interface{}) error {
    for k, v := range src {

        if _, ok := dst[k]; !ok {
            dst[k] = v // easy case - dst key does not exist
            continue
        }

        dm, ok1 := dst[k].(map[string]interface{})
        sm, ok2 := src[k].(map[string]interface{})

        if ok1 && ok2 {
            overlay2(dm, sm) // merge case
            continue
        }

        ds, ok1 := dst[k].([]interface{})
        ss, ok2 := src[k].([]interface{})

        if ok1 && ok2 {
            dst[k] = append(ds, ss...) // JSON array case
            continue
        }

        return fmt.Errorf("unhandled type/update")

    }
    return nil
}

https://play.golang.org/p/i-0yXMcqU7Z

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM