JSON字符串的交集 [英] Intersection of JSON strings

查看:98
本文介绍了JSON字符串的交集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试找到一种方法来将一个JSON字符串用作某种模板",以应用于另一个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"
   }
}

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

which I then apply to the following JSON string:

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

我想要一个生成的JSON字符串,如下所示:

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)...)

然后我要编写第二个函数,该函数使用这部分节点名称从输入的JSON字符串构造JSON字符串,但是用尽了精力,开始认为我可能还是走错了路.

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.

推荐答案

只需创建一个函数即可根据模板和源地图克隆"地图.

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

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

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:

  • 如果v不是地图,只需从源地图获取k键的值,然后在目标位置使用它.

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

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

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.

它是这样的:

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
}

仅此而已.

测试:

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

使用示例JSON输出(在游乐场上尝试):

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

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

注释:

解决方案假定源映射符合模板.也就是说,如果模板包含某个键的映射,则源映射也应包含相同键的映射.如果不能保证这样做,则应对procMap()函数进行扩展检查,以避免运行时出现恐慌,如下所示:

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

还请注意,JSON数组(切片)不会以任何特殊方式处理,这意味着如果模板包含切片,则按原样使用源中的值,并且如果切片包含地图,则不会进行递归.该解决方案也可以轻松扩展为处理切片,这是读者的练习.

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.

这篇关于JSON字符串的交集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆