Home  >  Article  >  Backend Development  >  Manually create json object from struct in golang

Manually create json object from struct in golang

WBOY
WBOYforward
2024-02-12 11:03:081067browse

Manually create json object from struct in golang

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!

Question content

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>

Workaround

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!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete