我正在尝试从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
任何提示请解决此问题
答案 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.builder
和prefix
,所以我编写了一个包装它的函数:
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
。
答案 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