0

How do i use the following struct to build a nested json recursively?

The nested struct can go to as many levels as it can.

Both the sample struct and json is mentioned below.

I'm having trouble in building a nested json/object dynamically.

I used the reflect package to access the struct.

I'm able to read through the data but not able to build the same.

type Data struct {
    ID string 
    Name string 
    Types *Details
}

type Details struct {
    Customer int32 
    Countries   int32       
}

To:

{
  "Name":"Data",
  "Fields":[{
    "Name":"ID",
    "Type":"string"
  },
  {
    "Name":"Name",
    "Type":"string"
  },
  {
    "Name":"Details",
    "Type":"struct",
    "Fields":[{
      "Name":"Customer",
      "Type":"int32"
    },{
      "Name":"Countries",
      "Type":"int32"
    }]
}

And whatever I have done so far, I have attached below:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func getFields(t reflect.Type, prefix string) {
    switch t.Kind() {
    case reflect.Ptr:
        getFields(t.Elem(), "")
    case reflect.Struct:
        buildRecursiveFunction(t)
    }
}

func buildRecursiveFunction(t reflect.Type) map[string]interface{} {
    var jsonArr []interface{}
    temp := make(map[string]interface{})
    for i := 0; i < t.NumField(); i++ {
        sf := t.Field(i)
        if sf.Name != "state" && sf.Name != "unknownFields" && sf.Name != "sizeCache" {
            obj := make(map[string]interface{})
            varName := sf.Name
            varType := sf.Type.Kind()
            obj["Name"] = varName
            obj["Type"] = varType.String()

            if varType.String() == "ptr" {
                obj["Fields"] = buildRecursiveFunction(sf.Type.Elem())
            } else {
            }
            jsonArr = append(jsonArr, obj)
        }
    }
    jsonArrVal, _ := json.Marshal(jsonArr)
    fmt.Println(string(jsonArrVal))
    return temp

}

func main() {
    getFields(reflect.TypeOf(&DeviceEnv{}), "")
    // json.Marshal(AllAttributes)
}

Any help is appreciated.

1
  • Note that in your "TO" example you've shown the string and bool fields as "Type": "struct". Presumably they should be "string" and "bool". Commented Jun 28, 2022 at 0:08

1 Answer 1

3

I've hacked around your code and got it (kind of) working here, but it doesn't seem like it's structured in the best way. Better to return and build a recursive object than trying to print it (with a prefix?) as you go.

What about defining a TypeInfo struct and having a recursive function that populates that? I think that leads to a clear structure, and it allows the caller to JSON-marshal the result as they want to:

func main() {
    info := MakeTypeInfo("DeviceEnvironment", DeviceEnv{})
    b, err := json.MarshalIndent(info, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s\n", b)
}

type TypeInfo struct {
    Name   string     `json:"Name"`
    Type   string     `json:"Type"`
    Fields []TypeInfo `json:"Fields,omitempty"`
}

func MakeTypeInfo(name string, value interface{}) TypeInfo {
    return makeTypeInfo(name, reflect.TypeOf(value))
}

func makeTypeInfo(name string, t reflect.Type) TypeInfo {
    kind := t.Kind()
    switch kind {
    case reflect.Struct:
        var fields []TypeInfo
        for i := 0; i < t.NumField(); i++ {
            field := t.Field(i)
            fields = append(fields, makeTypeInfo(field.Name, field.Type))
        }
        return TypeInfo{Name: name, Type: kind.String(), Fields: fields}
    case reflect.Pointer:
        return makeTypeInfo(name, t.Elem())
    default:
        return TypeInfo{Name: name, Type: kind.String()}
    }
}

Note that I haven't done the field filtering (eg: "unknownFields") that you've shown in your code -- shouldn't be hard to add inside the reflect.Struct case though.

Full example in Go Playground.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.