如何将包含嵌套结构的结构体展平为 JSON 格式?

huangapple go评论80阅读模式
英文:

How to flatten a Struct with embedded structs to json

问题

给定以下结构类型,StructAStructB嵌入在CompleteStruct中:

type StructA struct {
	A int `json:"a_a"`
	B int `json:"a_b"`
	C int `json:"a_c"`
}
type StructB struct {
	A int `json:"b_a"`
	B int `json:"b_b"`
}

type CompleteStruct struct {
	Name string `json:"name"`
	StructA
	StructB
}

还有一个新的结构体s

s := CompleteStruct{Name: "Example",
	StructA: StructA{
		A: 1,
		B: 2,
		C: 3,
	},
	StructB: StructB{
		A: 4,
		B: 5,
	},
}

你想要将s转换为以下JSON格式:

[
  {
    "name": "Example",
    "field": "a_a",
    "value": 1
  },
  {
    "name": "Example",
    "field": "a_b",
    "value": 2
  },
  {
    "name": "Example",
    "field": "a_c",
    "value": 3
  },
  {
    "name": "Example",
    "field": "b_a",
    "value": 4
  },
  {
    "name": "Example",
    "field": "b_b",
    "value": 5
  }
]

注意:实际上,CompleteStruct将包含10个或更多嵌入的结构体,每个嵌入的结构体将包含10个或更多字段。因此,我希望找到一种不需要逐个键入每个字段的解决方案,我认为这将需要使用反射。

英文:

Given the following struct types, StructA and StructB that are embedded in CompleteStruct

type StructA struct {
	A int `json:"a_a"`
	B int `json:"a_b"`
	C int `json:"a_c"`
}
type StructB struct {
	A int `json:"b_a"`
	B int `json:"b_b"`
}

type CompleteStruct struct {
	Name string `json:"name"`
	StructA
	StructB
}

And s which is a new struct.

s := CompleteStruct{Name: "Example",
	StructA: StructA{
		A: 1, 
		B: 2, 
		C: 3,
	},
	StructB: StructB{
		A: 4,
		B: 5,
	},
}

How do you transform s to the following json.

[
  {
    "name": "Example",
    "field": "a_a",
    "value": 1
  },
  {
    "name": "Example",
    "field": "a_b",
    "value": 2
  },
  {
    "name": "Example",
    "field": "a_c",
    "value": 3
  },
  {
    "name": "Example",
    "field": "b_a",
    "value": 4
  },
  {
    "name": "Example",
    "field": "b_b",
    "value": 5
  }
]

Note: In reality, CompleteStruct will contain 10 or more embedded structs and each embedded struct will contain 10 or more fields. So I would like a solution that does not require typing each field out individually, I assume this will require using reflection

答案1

得分: 2

你不能没有反射来解决这个问题。这是一个简单的例子:

func (u *CompleteStruct) MarshalJSON() ([]byte, error) {
	type Result struct {
		Name  string `json:"name"`
		Field string `json:"field"`
		Value any    `json:"value"`
	}
	
	var res []Result
	val := reflect.ValueOf(u).Elem()
	for i := 0; i < val.NumField(); i++ {
		field := val.Field(i)
		switch field.Kind() {
		case reflect.Struct:
			for i := 0; i < field.NumField(); i++ {
				tp := field.Type().Field(i)
				field := field.Field(i)
				res = append(res, Result{
					Name:  u.Name,
					Field: tp.Tag.Get("json"),
					Value: field.Interface(),
				})
			}
		}
	}
	return json.Marshal(res)
}

PLAYGROUND

英文:

You can't solve it without reflection. Simple example:

func (u *CompleteStruct) MarshalJSON() ([]byte, error) {
	type Result struct {
		Name  string `json:&quot;name&quot;`
		Field string `json:&quot;field&quot;`
		Value any    `json:&quot;value&quot;`
	}
	
	var res []Result
	val := reflect.ValueOf(u).Elem()
	for i := 0; i &lt; val.NumField(); i++ {
		field := val.Field(i)
		switch field.Kind() {
		case reflect.Struct:
			for i := 0; i &lt; field.NumField(); i++ {
				tp := field.Type().Field(i)
				field := field.Field(i)
				res = append(res, Result{
					Name:  u.Name,
					Field: tp.Tag.Get(&quot;json&quot;),
					Value: field.Interface(),
				})
			}
		}
	}
	return json.Marshal(res)
}

<kbd>PLAYGROUND</kbd>

答案2

得分: 0

这应该给你想要的结构:

package main

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

type StructA struct {
	A int `json:"a_a"`
	B int `json:"a_b"`
	C int `json:"a_c"`
}
type StructB struct {
	A int `json:"b_a"`
	B int `json:"b_b"`
}

type CompleteStruct struct {
	Name     string `json:"name"`
	StructA  StructA
	StructB  StructB
}

func main() {
	s := CompleteStruct{Name: "Example",
		StructA: StructA{
			A: 1,
			B: 2,
			C: 3,
		},
		StructB: StructB{
			A: 4,
			B: 5,
		},
	}

	flat(s)

	json.NewEncoder(os.Stdout).Encode(results)
}

type resp struct {
	Name  string      `json:"name"`
	Field string      `json:"field"`
	Value interface{} `json:"value"`
}

var globalName string
var results []resp

func flat(s interface{}) {
	st := reflect.TypeOf(s)
	for i := 0; i < st.NumField(); i++ {
		field := st.Field(i)
		if field.Type.Kind() == reflect.Struct {
			flat(reflect.ValueOf(s).Field(i).Interface())
		} else {
			name := field.Tag.Get("json")
			if name == "name" {
				globalName = reflect.ValueOf(s).Field(i).String()
				continue
			}
			results = append(results, resp{Name: globalName, Field: name, Value: reflect.ValueOf(s).Field(i).Interface()})
		}
	}
}
go run ./main.go | jq '.'

Go Playground

英文:

This should give you the structure you want:

package main

import (
	&quot;encoding/json&quot;
	&quot;os&quot;
	&quot;reflect&quot;
)

type StructA struct {
	A int `json:&quot;a_a&quot;`
	B int `json:&quot;a_b&quot;`
	C int `json:&quot;a_c&quot;`
}
type StructB struct {
	A int `json:&quot;b_a&quot;`
	B int `json:&quot;b_b&quot;`
}

type CompleteStruct struct {
	Name string `json:&quot;name&quot;`
	StructA
	StructB
}

func main() {
	s := CompleteStruct{Name: &quot;Example&quot;,
		StructA: StructA{
			A: 1,
			B: 2,
			C: 3,
		},
		StructB: StructB{
			A: 4,
			B: 5,
		},
	}

	flat(s)

	json.NewEncoder(os.Stdout).Encode(results)
}

type resp struct {
	Name  string `json:&quot;name&quot;`
	Field string `json:&quot;field&quot;`
	Value any    `json:&quot;value&quot;`
}

var globalName string
var results []resp

func flat(s interface{}) {
	st := reflect.TypeOf(s)
	for i := 0; i &lt; st.NumField(); i++ {
		field := st.Field(i)
		if field.Type.Kind() == reflect.Struct {
			flat(reflect.ValueOf(s).Field(i).Interface())
		} else {
			name := field.Tag.Get(&quot;json&quot;)
			if name == &quot;name&quot; {
				globalName = reflect.ValueOf(s).Field(i).String()
				continue
			}
			results = append(results, resp{Name: globalName, Field: name, Value: reflect.ValueOf(s).Field(i).Interface()})
		}
	}
}
go run ./main.go | jq &#39;.&#39;

Go Playground

huangapple
  • 本文由 发表于 2022年9月29日 14:31:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/73891190.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定