【问题标题】:Intersection of JSON stringsJSON字符串的交集
【发布时间】:2018-10-07 16:02:56
【问题描述】:

我正在尝试找到一种方法,将一个 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 字符串,但动力不足,并开始认为我可能走错了路。

对此的任何帮助将不胜感激。

【问题讨论】:

    标签: json string go


    【解决方案1】:

    只需创建一个基于模板和源地图“克隆”地图的函数。

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

    • 如果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() 函数并进行检查以避免运行时恐慌,如下所示:

    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 数组(切片)没有以任何特殊方式处理,这意味着如果模板包含切片,则源中的值按原样使用,如果切片包含映射,则不会发生递归。该解决方案也可以很容易地扩展到处理切片,这留给读者作为练习。

    【讨论】:

    • 感谢此代码。但是,我希望为嵌套的 json 数组增强它。我尝试了递归,但无法让它工作。例如,我希望 procMap 处理以下 json : { "assetId":"", "lineItems":[ {"lineItemId":"", "Expenses":"", "Date":"", "comment ": [ {"userId":"","comment":"","timestamp":""},...] }, .... ] } 注意:我的 json 有嵌套数组。目前我的程序每个数组行调用一次 procMap 。这会导致调用之前/之后的大量转换/恢复、冗余和脏代码。
    猜你喜欢
    • 1970-01-01
    • 2018-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多