JSON字符串的交集 [英] Intersection of JSON strings
问题描述
我正在尝试找到一种方法来将一个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 thek
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屋!