如何将map [string] interface {}转换为string

时间:2018-07-12 07:39:53

标签: go yaml

我正在尝试从json文件获取值的路径:

例如:

x: 4
z:
  y: 6
  t: 8
a:
  b:
    p: 0
m:
  c : 4 

我想要的输出是:

x = 4,z.y = 6,z.t = 8,a.b.p = 0,m.c = 4

我在下面写了一个函数:

func parsecustom(aMap map[string]interface{}) (string, string) {

    var sol string
    var k string
    for key, val := range aMap {
        switch concreteVal := val.(type) {
        case map[string]interface{}:
            x, _ := parseMap(val.(map[string]interface{}))
            sol = key + "." + x
            k += sol + ","
        case string:
            sol = key + "=" + concreteVal + " "
            k += sol + ","
        case float64:
            sol = key + "=" + strconv.FormatFloat(concreteVal, 'f', -1, 64) + " "
            k+= sol + ","
        default:
            k = " "

        }
    }

return sol, TrimSuffix(k, ",")
}

但没有考虑到这种情况:

z:
  y: 6
  t: 8

它覆盖,它只打印z.t = 8而忽略z.y = 6

任何提示请解决此问题

2 个答案:

答案 0 :(得分:2)

要解决子值应该带有父母名字的问题,我认为在递归时,需要有一个prefix参数。而且,与其让父母将这些前缀插入字符串中,还不如让孩子们像在编写自己的解析结果时那样写前缀,这更加正常。所以我更改了您的代码:

func parseMap(aMap map[string]interface{}, prefix string, b *strings.Builder) {
    var sol string
    for key, val := range aMap {
        switch concreteVal := val.(type) {
        case map[string]interface{}:
            parseMap(concreteVal,prefix+key+".",b)
        case string:
            sol = key + "=" + concreteVal + " "
            b.WriteString(prefix)
            b.WriteString(sol)
            b.WriteRune(',')
        case float64:
            sol = key + "=" + strconv.FormatFloat(concreteVal, 'f', -1, 64) + " "
            b.WriteString(prefix)
            b.WriteString(sol)
            b.WriteRune(',')
        default:
            //What?
            panic("Unsupported")
        }
    }
}

由于我们在此处包含很多字符串,因此我使用strings.Builder进行字符串构建。 b.WriteString(x)只是将x固定在字符串b的尾部。

由于我们现在使用strings.builderprefix,所以我编写了一个包装它的函数:

func parsecustom(aMap map[string]interface{}) string {
    b:=&strings.Builder{}
    parseMap(aMap,"",b)

    return strings.TrimSuffix(b.String(),",")
}

现在,如果您运行fmt.Println(parsecustom(data)),则可以得到输出:x=4 ,z.t=8 ,z.y=6 ,a.b.p=0 ,m.c=4

完整示例:https://play.golang.org/p/_QIp2LRYjm7

答案 1 :(得分:1)

在此部分的递归中,您只考虑sol的返回值,它是要解析的 last 元素。

    case map[string]interface{}:
        x, _ := parseMap(val.(map[string]interface{}))

给出值(假设它们将按顺序进行迭代,由于映射是无序的,因此并非严格如此):

z:
  y: 6
  t: 8

您将最终从递归调用中返回:

sol:  "t=8" 
k:    "y=6 ,t=8 ,"

然后,您获得第一个返回值sol,将其称为变量x,并在其前面加上"z."。这意味着您将忽略"y=6"中的k部分。您只考虑使用sol,它是递归调用要解析的最后一个值。

这是一个可运行的示例,从问题中的代码粘贴粘贴: https://play.golang.org/p/F8gwq9YMxWu

更新-解决方案

与其尝试从每个调用中返回多个内容以进行解析,不如传递一个字符串(一个前缀),该字符串应添加到调用堆栈下端的所有值中,将更加容易。这样,返回的值已经具有所需的最终值。并且可以将其添加到结果字符串中。

func parse(prefix string, m map[string]interface{}) string {
    if len(prefix) > 0 { // only add the . if this is not the first call.
        prefix = prefix + "."
    }

    // builder stores the results string, appended to it 
    var builder string
    for mKey, mVal := range m {

        // update a local prefix for this map key / value combination
        pp := prefix + mKey

        switch typedVal := mVal.(type) {
        case string:
            builder += fmt.Sprintf("%s%s, ", pp, typedVal)
        case float64:
            builder += fmt.Sprintf("%s.%-1.0f, ", pp, typedVal)
        case map[string]interface{}:
            // add all the values to the builder, you already know they are correct.
            builder += parse(pp, typedVal)
        }
    }

    // return the string that this call has built
    return builder
}

要首次调用它,请传递前缀""

result := parse("", m)

可运行示例解决方案 https://play.golang.org/p/Y-m9rQCY0xw