In golang, manually creating a json object from a struct is a common operation. By converting struct to json format, we can easily use it in network transmission or storage. In this article, PHP editor Banana will introduce you how to use golang's built-in package to achieve this function. Not only that, we will also explore how to deal with nested fields in structs and how to deal with special types of fields. Whether you are a beginner or an experienced developer, this article will provide you with detailed guidance to help you easily create json objects in golang. let's start!
I have a structure to say
<code>type Foo struct { A string `json:",omitemtpy" } </code>
I know I can easily convert it to json using something like this
json.Marshal(Foo{})
It will return an empty json string.
But I need to use the same structure to return the json representation of the structure with all the fields and "null values" present in the json. (Actually, it's a very large structure, so I can't just keep a copy without tags)
What's the easiest way?
Basically, I need to create a json marshal of a structure that ignores the json omitempty tag.
This json creation does not need to be efficient or performant.
I would have preferred a library for this kind of task, but most libraries I've seen either create some special format or respect omitempty
edit:
Choose https://stackoverflow.com/a/77799949/2187510 as my answer and do some extra work to allow default values (using its code as a reference)
defaultFoo := FoodWithPts{ Str: "helloWorld"} dupFooType := dupType(reflect.TypeOf(defaultFoo)) foo := reflect.Zero(dupFooType).Interface() // New additions defaults, _ := json.Marshal(defaultFoo) json.Unmarshal(defaults, &foo) // overwrites foo with defaults // End New additions data, err := json.Marshal(foo) fmt.Println("dup FooWithPtrs:\n", string(data), err)
Output:
dup FooWithPtrs: {"String":"helloWorld","Int":0,"Bar":null} <nil>
You cannot modify tags at runtime, but you can use $$c to create struct types at runtime$$reflect.StructOf().
So the idea is to copy the struct type but exclude the ,omitempty
option from the JSON tag in the duplication.
You can find all examples below on Go Playground.
This is easier than people first think. We just need to do it recursively (one struct field might be another struct), and we should definitely deal with pointers:
func dupType(t reflect.Type) reflect.Type { if t.Kind() == reflect.Pointer { return reflect.PointerTo(dupType(t.Elem())) } if t.Kind() != reflect.Struct { return t } var fields []reflect.StructField for i := 0; i < t.NumField(); i++ { sf := t.Field(i) sf.Type = dupType(sf.Type) // Keep json tag but cut ,omitempty option if exists: if tag, _ := strings.CutSuffix(sf.Tag.Get("json"), ",omitempty"); tag == "" { sf.Tag = "" } else { sf.Tag = `json:"` + reflect.StructTag(tag) + `"` } fields = append(fields, sf) } return reflect.StructOf(fields) }
Let's test it with this type:
type Foo struct { Str string `json:"String,omitempty"` Int int `json:",omitempty"` Bar struct { Float float64 `json:",omitempty"` PtrInt int `json:",omitempty"` Baz struct { X int `json:"XXXX,omitempty"` } `json:",omitempty"` } `json:",omitempty"` }
First, here is the JSON output without type duplication:
data, err := json.Marshal(Foo{}) fmt.Println("Foo:\n", string(data), err)
Output:
Foo: {"Bar":{"Baz":{}}} <nil>
Note that we get the Bar
and Baz
fields because they are structs.
Let’s try type copy:
dupFooType := dupType(reflect.TypeOf(Foo{})) foo := reflect.Zero(dupFooType).Interface() data, err := json.Marshal(foo) fmt.Println("dup Foo:\n", string(data), err)
This will output:
dup Foo: {"String":"","Int":0,"Bar":{"Float":0,"PtrInt":0,"Baz":{"XXXX":0}}} <nil>
good! Exactly what we wanted!
But we're not done yet. What if we have a type with a structure pointer field? like this:
type FooWithPtrs struct { Str string `json:"String,omitempty"` Int int `json:",omitempty"` Bar *struct { Float float64 `json:",omitempty"` PtrInt int `json:",omitempty"` Baz *struct { X int `json:"XXXX,omitempty"` } `json:",omitempty"` } `json:",omitempty"` }
Attempt to JSON marshal values of repeated types:
dupFooType := dupType(reflect.TypeOf(FooWithPtrs{})) foo := reflect.Zero(dupFooType).Interface() data, err := json.Marshal(foo) fmt.Println("dup FooWithPtrs:\n", string(data), err)
Output:
dup FooWithPtrs: {"String":"","Int":0,"Bar":null} <nil>
If the structure contains pointers, these pointers appear in the JSON output as null
, but we would like their fields to appear in the output as well. This requires them to be initialized to non-nil
values in order for them to produce output.
Fortunately, we can also use reflection to do this:
func initPtrs(v reflect.Value) { if !v.CanAddr() { return } if v.Kind() == reflect.Pointer { v.Set(reflect.New(v.Type().Elem())) v = v.Elem() } if v.Kind() == reflect.Struct { for i := 0; i < v.NumField(); i++ { initPtrs(v.Field(i)) } } }
We are excited! Let’s see it in action:
dupFooType := dupType(reflect.TypeOf(FooWithPtrs{})) fooVal := reflect.New(dupFooType) initPtrs(fooVal.Elem()) data, err := json.Marshal(fooVal.Interface()) fmt.Println("dup and inited FooWithPtrs:\n", string(data), err)
Output:
dup and inited FooWithPtrs: {"String":"","Int":0,"Bar":{"Float":0,"PtrInt":0,"Baz":{"XXXX":0}}} <nil>
good! It contains all fields!
The above is the detailed content of Manually create json object from struct in golang. For more information, please follow other related articles on the PHP Chinese website!