我正在尝试找到一种方法来将一个JSON字符串用作各种“模板”以应用于另一个JSON字符串。例如,如果我的模板如下所示:
{ "id": "1", "options": { "leatherseats": "1", "sunroof": "1" } }
然后将其应用于以下JSON字符串:
{ "id": "831", "serial": "19226715", "options": { "leatherseats": "black", "sunroof": "full", "fluxcapacitor": "yes" } }
我想要如下所示的结果JSON字符串:
{ "id": "831", "options": { "leatherseats": "black", "sunroof": "full", } }
不幸的是,我既不能依赖模板也不可以是固定格式的输入,因此我无法编组/解组到已定义的接口中。
我已经编写了一个遍历模板的递归函数,以构造一个带有每个要包含的节点名称的字符串切片。
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 }
我称这个函数如下:
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字符串,但是用尽了精力,开始思考我可能还是走错了路。
任何帮助,将不胜感激。
只需创建一个基于模板和源地图“克隆”地图的函数。
该解决方案将遍历模板映射的条目,并为每一(k, v)对在目标映射中生成一个条目,如下所示:
(k, v)
如果v不是地图,只需k从源地图获取键的值,然后在目标位置使用它。
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 }
就这样。
测试它:
// 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(在Go Playground上尝试):
{ "id": "831", "options": { "leatherseats": "black", "sunroof": "full" } }
笔记:
该解决方案 假定 源映射符合模板。也就是说,如果模板包含某个键的映射,则源映射也应包含相同键的映射。如果不能保证,procMap()则应通过检查扩展该函数,以避免运行时出现恐慌,如下所示:
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] } }
还要注意,JSON数组(切片)不会以任何特殊方式处理,这意味着如果模板包含切片,则按原样使用源中的值,并且如果切片包含地图,则不会进行递归。该解决方案也可以轻松扩展为处理切片,这留给读者练习。