英文:
How to build a nested json/object recursively from a nested struct in Golang
问题
如何使用以下结构来递归地构建嵌套的 JSON?
嵌套的结构可以有多个级别。
下面提到了示例结构和 JSON。
我在动态构建嵌套的 JSON/对象方面遇到了问题。
我使用了 reflect 包来访问结构。
我能够遍历数据,但无法构建相同的结构。
type Data struct {
ID string
Name string
Types *Details
}
type Details struct {
Customer int32
Countries int32
}
转换为:
{
"Name":"Data",
"Fields":[{
"Name":"ID",
"Type":"string"
},
{
"Name":"Name",
"Type":"string"
},
{
"Name":"Details",
"Type":"struct",
"Fields":[{
"Name":"Customer",
"Type":"int32"
},{
"Name":"Countries",
"Type":"int32"
}]
}
我所做的工作如下:
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)
}
任何帮助都将不胜感激。
英文:
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
得分: 3
我已经在你的代码中进行了一些修改,并使其(在某种程度上)工作这里,但它似乎并不是以最佳方式结构化的。最好的方式是返回并构建一个递归对象,而不是尝试在进行过程中打印它(带有前缀?)。
关于定义一个TypeInfo
结构体并拥有一个递归函数来填充它,你怎么看?我认为这会导致一个清晰的结构,并且允许调用者按照他们的意愿对结果进行JSON编组:
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()}
}
}
请注意,我还没有完成你在代码中展示的字段过滤(例如:"unknownFields")--但在reflect.Struct
情况下添加它应该不难。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论