JSON字符串的交集

Dew*_*oel 5 string json go

我正在尝试找到一种方法来使用一个JSON字符串作为各种"模板"来应用于另一个JSON字符串.例如,如果我的模板如下所示:

{
   "id": "1",
   "options": {
      "leatherseats": "1",
      "sunroof": "1"
   }
}
Run Code Online (Sandbox Code Playgroud)

然后我将其应用于以下JSON字符串:

{
   "id": "831",
   "serial": "19226715",
   "options": {
      "leatherseats": "black",
      "sunroof": "full",
      "fluxcapacitor": "yes"
   }
}
Run Code Online (Sandbox Code Playgroud)

我想要一个结果JSON字符串如下:

{
   "id": "831",
   "options": {
      "leatherseats": "black",
      "sunroof": "full",
   }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我不能依赖模板和输入是固定格式的,所以我不能编组/解组成定义的接口.

我编写了一个递归函数,遍历模板以构造一个字符串片段,其中包含要包含的每个节点的名称.

func traverseJSON(key string, value interface{}) []string {
    var retval []string
    unboxed, ok := value.(map[string]interface{})
    if ok {
        for newkey, newvalue := range unboxed {
            retval = append(retval, recurse(fmt.Sprintf("%s.%s", key, newkey), newvalue)...)
        }
    } else {
        retval = append(retval, fmt.Sprintf("%s", key))
    }
    return retval
}
Run Code Online (Sandbox Code Playgroud)

我把这个函数称为如下:

template := `my JSON template here`
var result map[string]interface{}
json.Unmarshal([]byte(template), &result)

var nodenames []string
nodenames = append(nodenames, traverseJSON("", result)...)
Run Code Online (Sandbox Code Playgroud)

然后我要编写第二个函数,它接受这片节点名称来从输入JSON字符串构造一个JSON字符串,但是已经失去了动力并开始认为我可能在错误的轨道上.

任何有关这方面的帮助将不胜感激.

icz*_*cza 4

只需创建一个基于模板和源地图"克隆"地图的功能.

解决方案将迭代模板映射的条目,并为每个(k, v)对在目标映射中生成一个条目,如下所示:

  • 如果v不是地图,只需k从源地图中获取密钥的值,并在目标中使用它.

  • 如果v也是一个映射,则以新模板映射为递归方式调用此"克隆器" v,新源是来自k密钥源的值.此递归调用的结果将是k目标映射中键的值.

这是它的样子:

func procMap(tmpl, src map[string]interface{}) (dst map[string]interface{}) {
    dst = map[string]interface{}{}

    for k, v := range tmpl {
        if innerMap, ok := v.(map[string]interface{}); ok {
            dst[k] = procMap(innerMap, src[k].(map[string]interface{}))
        } else {
            dst[k] = src[k]
        }
    }

    return dst
}
Run Code Online (Sandbox Code Playgroud)

就这样.

测试它:

// tmpljson is the template JSON
var tmpl map[string]interface{}
if err := json.Unmarshal([]byte(tmpljson), &tmpl); err != nil {
    panic(err)
}

// srcjson is the source JSON
var src map[string]interface{}
if err := json.Unmarshal([]byte(srcjson), &src); err != nil {
    panic(err)
}

dst := procMap(tmpl, src)

enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", "  ")
if err := enc.Encode(dst); err != nil {
    panic(err)
}
Run Code Online (Sandbox Code Playgroud)

使用示例JSON输出(在Go Playground上尝试):

{
  "id": "831",
  "options": {
    "leatherseats": "black",
    "sunroof": "full"
  }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

解决方案假定源映射符合模板.也就是说,如果模板包含某个键的映射,则源映射还应包含相同键的映射.如果无法保证这一点,则procMap()应该通过检查来扩展该函数,以避免运行时出现紧急情况,如下所示:

for k, v := range tmpl {
    if innerMap, ok := v.(map[string]interface{}); ok {
        if src2, ok2 := src[k].(map[string]interface{}); ok2 {
            dst[k] = procMap(innerMap, src2)
        } else {
            log.Printf("src is not conform to template at key %q", k)
        }
    } else {
        dst[k] = src[k]
    }
}
Run Code Online (Sandbox Code Playgroud)

另请注意,JSON数组(切片)不会以任何特殊方式处理,这意味着如果模板包含切片,则源的值将按原样使用,如果切片包含贴图,则不会发生递归.解决方案也可以很容易地扩展到处理切片,这留给读者练习.