golang json and slices of interface

久未见 提交于 2019-12-20 04:50:23

问题


I am having trouble iterating over slices of interfaces that contain slices of interfaces.

This problem has arisen through attempting to work with an API call which returns JSON data. There is quite a lot of data returned and the structure differs quite dramatically depending on the request. There is also no structure for the JSON responses in the API documentation so I am trying to implement some methods for working with arbitrary JSON responses.

Presently when the initial call is made it is dropped into a map[string]interface{} and then a switch statement is run to determine the type of each element, the problem occurs when a slice of interfaces is encountered. I can't seem to do anything with them.

I have tried using the sort package a few times (specifically the sort and slicestable functions) to no avail.

The error I am receiving is:

interface conversion: interface {} is []interface {}, not map[string]interface {}

Which occurs when I try and map the slice of interfaces so I can iterate over them with the switch statement again.

output := make(map[string]interface{})
err = json.Unmarshal(body, &output)

fmt.Println(err)
identify(output)

return err
}

func identify(output map[string]interface{}) {
    fmt.Printf("%T", output)
    for a, b := range output {
        switch bb := b.(type) {
        case string:
            fmt.Println("This is a string")
        case float64:
            fmt.Println("this is a float")
        case []interface{}:
            fmt.Println(" is []interface ", bb)
            test := b.(map[string]interface{}) // falis here
            fmt.Println("Success!", test)
        default:
            return
        }
    }
}

So the basic question is how do I iterate over nested slices of interfaces without knowing the structure beforehand?


回答1:


You can add a switch case which is checking the type of interface for slice of interfaces and then run the same function as recursive until whole json is parsed.

output := make(map[string]interface{})
err = json.Unmarshal(body, &output)

fmt.Println(err)
identify(output)

return err
}

func identify(output map[string]interface{}) {
    fmt.Printf("%T", output)
    for a, b := range output {
        switch bb := b.(type) {
        case string:
            fmt.Println("This is a string")
        case float64:
            fmt.Println("this is a float")
        case []interface{}:
        // Access the values in the JSON object and place them in an Item
        for _, itemValue := range jsonObj {
            fmt.Printf("%v is an interface\n", itemValue)
            identify(itemValue.(map[string]interface{}))
        }
        default:
            return
        }
    }
}

There can be deep nested json. We just needs to create options for each case until json is completely parsed.




回答2:


Well, you can't cast []interface{} to map[string]interface{}. Since it's a slice, you can iterate over its elements (note that bb is b already cast to the appropriate type and you don't need to cast b again):

    case []interface{}:
        fmt.Println(" is []interface ", bb)
        for _, val := range bb {
            // do something with val
        }
    default:
    ...

Why do you have to deal with something that you don't know? Is it possible that you reconsider your architecture and work with known type(s)? have you seen the example of delaying unmarshaling using json.RawMessage? Or maybe try implementing the Unmarshaler interface?



来源:https://stackoverflow.com/questions/48167286/golang-json-and-slices-of-interface

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!