Home >Backend Development >Golang >How to Flatten Anonymous Interface Fields in Go JSON Serialization?

How to Flatten Anonymous Interface Fields in Go JSON Serialization?

Susan Sarandon
Susan SarandonOriginal
2024-10-29 06:52:02558browse

How to Flatten Anonymous Interface Fields in Go JSON Serialization?

Flattening JSON Structs with Anonymous Members in Go

In Go, anonymous struct fields are typically marshaled as if their inner exported fields were fields in the outer struct. However, this can lead to unexpected behavior when marshaling structs with anonymous members of type interface{}.

The Problem

Consider the following example:

<code class="go">type User struct {
    Id   int    `json:"id"`
    Name string `json:"name"`
}

type Session struct {
    Id     int `json:"id"`
    UserId int `json:"userId"`
}

type Anything interface{}

type Hateoas struct {
    Anything
    Links map[string]string `json:"_links"`
}

func MarshalHateoas(subject interface{}) ([]byte, error) {
    h := &Hateoas{subject, make(map[string]string)}
    switch s := subject.(type) {
    case *User:
        h.Links["self"] = fmt.Sprintf("http://user/%d", s.Id)
    case *Session:
        h.Links["self"] = fmt.Sprintf("http://session/%d", s.Id)
    }
    return json.MarshalIndent(h, "", "    ")
}

func main() {
    u := &User{123, "James Dean"}
    s := &Session{456, 123}
    json, err := MarshalHateoas(u)
    if err != nil {
        panic(err)
    } else {
        fmt.Println("User JSON:")
        fmt.Println(string(json))
    }
    json, err = MarshalHateoas(s)
    if err != nil {
        panic(err)
    } else {
        fmt.Println("Session JSON:")
        fmt.Println(string(json))
    }
}</code>

When running this code, the resulting JSON is not as expected:

<code class="json">User JSON:
{
    "Anything": {
        "id": 123,
        "name": "James Dean"
    },
    "_links": {
        "self": "http://user/123"
    }
}
Session JSON:
{
    "Anything": {
        "id": 456,
        "userId": 123
    },
    "_links": {
        "self": "http://session/456"
    }
}</code>

As you can see, the anonymous member Anything is treated as a named field in the JSON, which is not the intended behavior.

The Solution

To flatten the anonymous member and achieve the desired JSON structure, we can use the reflect package to loop over the fields of the struct and map them to a map[string]interface{}. This allows us to retain the flat structure of the original struct without introducing new fields.

The updated code is as follows:

<code class="go">import (
    "encoding/json"
    "fmt"
    "reflect"
)

// ... (rest of the code remains the same)

func MarshalHateoas(subject interface{}) ([]byte, error) {
    links := make(map[string]string)
    out := make(map[string]interface{})
    subjectValue := reflect.Indirect(reflect.ValueOf(subject))
    subjectType := subjectValue.Type()
    for i := 0; i < subjectType.NumField(); i++ {
        field := subjectType.Field(i)
        name := subjectType.Field(i).Name
        out[field.Tag.Get("json")] = subjectValue.FieldByName(name).Interface()
    }

    switch s := subject.(type) {
    case *User:
        links["self"] = fmt.Sprintf("http://user/%d", s.Id)
    case *Session:
        links["self"] = fmt.Sprintf("http://session/%d", s.Id)
    }

    out["_links"] = links
    return json.MarshalIndent(out, "", "    ")
}</code>

With this modification, the resulting JSON is correctly flattened:

<code class="json">User JSON:
{
    "id": 123,
    "name": "James Dean",
    "_links": {
        "self": "http://user/123"
    }
}
Session JSON:
{
    "id": 456,
    "userId": 123,
    "_links": {
        "self": "http://session/456"
    }
}</code>

The above is the detailed content of How to Flatten Anonymous Interface Fields in Go JSON Serialization?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
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