I'm trying to work on an extention for another software that sends a request to an application made in Go. In the Go program (which I'll now refer to as "the program"), one purpose is to convert a JSON file into a format that can be iterated through. Here is an example JSON format I am working with:
{
"name": "Game-Name",
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"$path": "src/ReplicatedStorage"
},
"ServerScriptService": {
"$path": "src/ServerScriptService"
},
"ReplicatedFirst": {
"$path": "src/ReplicatedFirst"
},
"ServerStorage": {
"$path": "src/ServerStorage"
}
}
}
The idea is that:
src
folder with the index of the parent map. For example, ReplicatedStorage
is the name of a folder that has the path src/ReplicatedStorage
Below is a process function intended to do this:
func process(in interface{}) {
v := reflect.ValueOf(in)
if v.Kind() == reflect.Map {
for _, key := range v.MapKeys() {
strct := v.MapIndex(key)
index := key.Interface()
value := reflect.ValueOf(strct.Interface())
if index == "tree" {
for _, treeKey := range value.MapKeys() {
treeIndex := treeKey.Interface()
fmt.Println("KEY")
fmt.Println(treeIndex)
if treeIndex != "$className" {
fmt.Println("bug")
fmt.Println(treeKey)
a := key.MapIndex(value) // panic serving ...: reflect: call of reflect.Value.MapIndex on string Value
b := reflect.ValueOf(a.Interface())
for _, key2 := range b.MapKeys() {
index2 := key2.Interface()
value2 := reflect.ValueOf(key2.Interface())
fmt.Println(index2)
fmt.Println(value2)
}
}
}
}
}
}
}
The comment is where and what the error is. One thing I would also want to ideally do is to not have to stack for-loops, since that's pretty stinky code.
The usual approach is to unmarshal to Go types that match the structure of the data. The problem here is that the tree cannot easily be represented as a Go type (it has field $classname with string type, but is otherwise like a map with object values containing $path field).
Let's proceed with unmarshaling to interface{}
as you have already done.
Use type assertions instead of the reflect package. Use map indexing to find values instead of looping through keys and looking for a match.
func process(in interface{}) error {
top, ok := in.(map[string]interface{})
if !ok {
return errors.New("expected object at top level")
}
tree, ok := top["tree"].(map[string]interface{})
if !ok {
return errors.New(".tree not found")
}
name, ok := top["name"]
if !ok {
return errors.New(".name not found")
}
className, ok := tree["$className"].(string)
if !ok {
return errors.New(".tree.$className not found")
}
for k, v := range tree {
thing, ok := v.(map[string]interface{})
if !ok {
continue
}
path, ok := thing["$path"].(string)
if !ok {
continue
}
fmt.Println(name, className, k, path)
}
return nil
}