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

Manually create json object from struct in golang

WBOY
Release: 2024-02-12 11:03:08
forward
1106 people have browsed it

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>
Copy after login

I know I can easily convert it to json using something like this

json.Marshal(Foo{})
Copy after login

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)
Copy after login

Output:

dup FooWithPtrs:
    {"String":"helloWorld","Int":0,"Bar":null} <nil>
Copy after login

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)
}
Copy after login

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"`
}
Copy after login

First, here is the JSON output without type duplication:

data, err := json.Marshal(Foo{})
fmt.Println("Foo:\n", string(data), err)
Copy after login

Output:

Foo:
 {"Bar":{"Baz":{}}} <nil>
Copy after login

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)
Copy after login

This will output:

dup Foo:
 {"String":"","Int":0,"Bar":{"Float":0,"PtrInt":0,"Baz":{"XXXX":0}}} <nil>
Copy after login

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"`
}
Copy after login

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)
Copy after login

Output:

dup FooWithPtrs:
 {"String":"","Int":0,"Bar":null} <nil>
Copy after login

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))
        }
    }
}
Copy after login

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)
Copy after login

Output:

dup and inited FooWithPtrs:
 {"String":"","Int":0,"Bar":{"Float":0,"PtrInt":0,"Baz":{"XXXX":0}}} <nil>
Copy after login

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!

Related labels:
source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template