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
CodePudding user response:
You can't solve it without reflection. Simple example:
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.Name,
Value: field.Interface(),
})
}
}
}
return json.Marshal(res)
}
CodePudding user response:
This should give you the structure you want:
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
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 any `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 '.'