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.

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

报告相同问题?

悬赏问题

  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能