Golang合并JSON

时间:2018-02-02 20:39:43

标签: json go

以下是我需要的一个例子:

默认JSON:

{
    "name": "John",
    "greetings": {
        "first": "hi",
        "second": "hello"
    }
}

与更改合并:

{
    "name": "Jane",
    "greetings": {
        "first": "hey"
    }
}

应该成为:

{
    "name": "Jane",
    "greetings": {
        "first": "hey",
        "second": "hello"
    }
}

以下是我尝试的内容:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

func MergeJSON(defaultJSON, changedJSON string) string {
    var defaultJSONDecoded map[string]interface{}
    defaultJSONUnmarshalErr := json.Unmarshal([]byte(defaultJSON), &defaultJSONDecoded)
    if defaultJSONUnmarshalErr != nil {
        panic("Error unmarshalling first JSON")
    }
    var changedJSONDecoded map[string]interface{}
    changedJSONUnmarshalErr := json.Unmarshal([]byte(changedJSON), &changedJSONDecoded)
    if changedJSONUnmarshalErr != nil {
        panic("Error unmarshalling second JSON")
    }
    for key, _ := range defaultJSONDecoded {
        checkKeyBeforeMerging(key, defaultJSONDecoded[key], changedJSONDecoded[key], changedJSONDecoded)
    }
    mergedJSON, mergedJSONErr := json.Marshal(changedJSONDecoded)
    if mergedJSONErr != nil {
        panic("Error marshalling merging JSON")
    }
    return string(mergedJSON)
}

func checkKeyBeforeMerging(key string, defaultMap interface{}, changedMap interface{}, finalMap map[string]interface{}) {
    if !reflect.DeepEqual(defaultMap, changedMap) {
        switch defaultMap.(type) {
        case map[string]interface{}:
            //Check that the changed map value doesn't contain this map at all and is nil
            if changedMap == nil {
                finalMap[key] = defaultMap
            } else if _, ok := changedMap.(map[string]interface{}); ok { //Check that the changed map value is also a map[string]interface
                defaultMapRef := defaultMap.(map[string]interface{})
                changedMapRef := changedMap.(map[string]interface{})
                for newKey, _ := range defaultMapRef {
                    checkKeyBeforeMerging(newKey, defaultMapRef[newKey], changedMapRef[newKey], finalMap)
                }
            }
        default:
            //Check if the value was set, otherwise set it
            if changedMap == nil {
                finalMap[key] = defaultMap
            }
        }
    }
}

func main() {
    defaultJSON := `{"name":"John","greetings":{"first":"hi","second":"hello"}}`
    changedJSON := `{"name":"Jane","greetings":{"first":"hey"}}`
    mergedJSON := MergeJSON(defaultJSON, changedJSON)
    fmt.Println(mergedJSON)
}

上面的代码返回以下内容:

{
    "greetings": {
        "first": "hey"
    },
    "name": "Jane",
    "second": "hello"
}

所以基本上任何更改都应该应用于默认值并返回完整的JSON。我还需要这个递归工作。

我该如何解决这个问题?我可以看到我出错的地方,我不知道如何让它以递归方式工作。

由于

2 个答案:

答案 0 :(得分:1)

发布的代码中的问题与您的递归调用有关:

checkKeyBeforeMerging(newKey, defaultMapRef[newKey], changedMapRef[newKey], finalMap)

finalMap的引用实际上应该是合并地图的嵌套部分。含义用finalMap替换finalMap[key].(map[string]interface{})

答案 1 :(得分:0)

我最近遇到了同样的需求,这是我的解决方案:

// override common json by input json
func Override(input, common interface{}) {
    switch inputData := input.(type) {
    case []interface{}:
        switch commonData := common.(type) {
        case []interface{}:
            for idx, v := range inputData {
                Override(v, commonData[idx])
            }
        }
    case map[string]interface{}:
        switch commonData := common.(type) {
        case map[string]interface{}:
            for k, v := range commonData {
                switch reflect.TypeOf(v).Kind() {
                case reflect.Slice, reflect.Map:
                    Override(inputData[k], v)
                default:
                    // do simply replacement for primitive type
                    _, ok := inputData[k]
                    if !ok {
                        inputData[k] = v
                    }
                }
            }
        }
    }
    return
}

在调用前解组json:

var commmon interface{}
if err = json.Unmarshal("common data", &commmon); err != nil {
    logger.Error(err)
    return
}

var input interface{}
if err = json.Unmarshal("input data", &input); err != nil {
    logger.Error(err)
    return
}

Override(input, commmon)