doukuo9116 2018-10-07 16:02 采纳率: 100%
浏览 136
已采纳

JSON字符串的交集

I'm trying to find a way to use one JSON string as a "template" of sorts to apply to another JSON string. For instance, if my template looks as follows:

{
   "id": "1",
   "options": {
      "leatherseats": "1",
      "sunroof": "1"
   }
}

which I then apply to the following JSON string:

{
   "id": "831",
   "serial": "19226715",
   "options": {
      "leatherseats": "black",
      "sunroof": "full",
      "fluxcapacitor": "yes"
   }
}

I'd like a resultant JSON string as follows:

{
   "id": "831",
   "options": {
      "leatherseats": "black",
      "sunroof": "full",
   }
}

Unfortunately I can't rely on either the template nor the input to be of a fixed format so I can't marshall/unmarshall into defined interfaces.

I got as far as writing a recursive function that traverses the template to construct a slice of string with the name of each node that is to be included.

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
}

I call this function as follows:

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

var nodenames []string
nodenames = append(nodenames, traverseJSON("", result)...)

I was then going to write a second function that takes this slice of node names to construct a JSON string from the input JSON string but ran out of steam and started thinking that I might be on the wrong track anyway.

Any help on this would be appreciated.

  • 写回答

1条回答 默认 最新

  • doushe7934 2018-10-07 19:48
    关注

    Simply create a function which "clones" a map based on a template and a source map.

    The solution would iterate over the entries of the template map, and for each (k, v) pair generate an entry in the destination map as follows:

    • If v is not a map, simply get the value for the k key from the source map, and use this in the destination.

    • If v is also a map, then call this "cloner" recursively with the new template map being v and the new source being the value from the source for the k key. The result of this recursive call will be the value for the k key in the destination map.

    This is how it could look like:

    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
    }
    

    And that's all.

    Testing it:

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

    Output with your example JSONs (try it on the Go Playground):

    {
      "id": "831",
      "options": {
        "leatherseats": "black",
        "sunroof": "full"
      }
    }
    

    Notes:

    The solution assumes the source map conforms to the template. That is, if the template contains a map for some key, the source map is also expected to contain a map for the same key. If this cannot be guaranteed, the procMap() function should be extended with a check to avoid a runtime panic, like this:

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

    Also note that JSON arrays (slices) are not treated in any special way, meaning if the template contains a slice, the value from the source is used as-is, and no recursion happens if the slice contains maps. The solution can easily be extended to handle slices too, which is left as an exercise for the reader.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 运筹学排序问题中的在线排序
  • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 lammps拉伸应力应变曲线分析
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥15 请问Lammps做复合材料拉伸模拟,应力应变曲线问题