简体   繁体   中英

Converting a complex struct to a simple json

Background

I have a struct that I have been passing to users as a json via an API. This works.

I am now trying to send the same struct as an object that my UI can easily convert to a csv and allow the user to download.

I have code that works on the UI to convert simple objects to a CSV. So I can send an object where the values are just integers or strings no problem. But, my struct has values that are objects and arrays. So, I am trying to find a way to convert my complex json object to a simpler one. The simpler object should only have values with either no quotes like an int or just one set of quotes on the outside like a string.

Question

How do I convert a golang struct into a json that has "simple" values (string, bool, int but not array or object with more quotes)?

Example

For instance this code converts user to a json with the formatting:

{
 "Name":"Frank",
 "Friends":[{"Age":10,"Name":"Joe"}, {"Age":12,"Name":"John"}],
 "FavSnacks":["apple","orange"]
}

, but I would like the formatting

 {
  "Name":"Frank",
  "Friends":"[{'Age':10,'Name':'Joe'},{'Age':12,'Name':'John'}]",
  "FavSnacks":"['apple','orange']"
 }

or

 {
  "Name":"Frank",
  "Friends":"[{Age:10, Name:Joe},{Age:12,Name:John}]",
  "FavSnacks":"[apple, orange]"
 }

Here is the code I have

package main

import (
    "fmt"
    "encoding/json"
)

type User struct {
    Name string
    Friends []Friend
    FavSnacks []string
}

type Friend struct {
    Age int
    Name string
}

func main() {
    user := &User{Name: "Frank", FavSnacks: []string{"apple","orange"}, Friends: []Friend{{Age: 10, Name: "Joe"},{Age: 12, Name: "John"}} }
    b, err := json.Marshal(user)
    if err != nil {
        fmt.Println(err)
        return
    }
     
    fmt.Println(string(b))
}

Notes

  1. I am using this site: https://play.golang.org/ to test the code above.
  2. It would be great if the function for this worked by looping through all struct fields since in real life I have a struct with around 30 fields some of which are structs that have nested structs.

Build a map with the values you want to marshal. Use the reflect API to iterate through the fields in the struct and collect the values in a map. If the value of the field is a slice or struct, then encode the field to JSON and use that value in the map. Return the map encoded to JSON.

func marshalFlat(value interface{}) ([]byte, error) {
    v := reflect.Indirect(reflect.ValueOf(value))
    t := v.Type()

    // Collect all fields in this map.
    m := make(map[string]interface{})
    for i := 0; i < t.NumField(); i++ {
        sf := t.Field(i)
        if sf.PkgPath != "" {
            // skip unexported
            continue
        }
        f := reflect.Indirect(v.Field(i))
        switch f.Kind() {
        case reflect.Slice, reflect.Struct:
            // Encode slices and structs as JSON and use
            // that JSON as the value of the field.
            p, err := json.Marshal(f.Interface())
            if err != nil {
                return nil, err
            }
            m[sf.Name] = string(p)
        default:
            // User other types as is.
            m[sf.Name] = f.Interface()
        }
    }
    return json.Marshal(m)
}

Run it on the playground

Just implement Marshaler interface:)

package main

import (
    "encoding/json"
    "fmt"
    "strings"
)

type User struct {
    Name string `json:"name"`
    Friends Friends `json:"friends"`
    FavSnacks FavSnacks `json:"fav_snacks"`
}

type Friend struct {
    Age int `json:"age"`
    Name string `json:"name"`
}

type Friends []Friend

func (f *Friends) MarshalJSON() ([]byte, error) {
    type FriendsAux []Friend

    friendsAux := FriendsAux(*f)

    marshal, err := json.Marshal(friendsAux)
    if err != nil {
        return []byte{}, nil
    }
    escapedMarshal := strings.ReplaceAll(string(marshal), `"`, `'`)

    jsonToString := fmt.Sprintf(`"%s"`, escapedMarshal)

    return []byte(jsonToString), nil
}

type FavSnacks []string

func (f *FavSnacks) MarshalJSON() ([]byte, error) {
    type FavSnacksAux []string

    favSnacksAux := FavSnacksAux(*f)

    marshal, err := json.Marshal(favSnacksAux)
    if err != nil {
        return []byte{}, nil
    }
    escapedMarshal := strings.ReplaceAll(string(marshal), `"`, `'`)

    jsonToString := fmt.Sprintf(`"%s"`, escapedMarshal)

    return []byte(jsonToString), nil
}


func main() {
    user := &User{Name: "Frank", FavSnacks: []string{"apple","orange"}, Friends: []Friend{{Age: 10, Name: "Joe"},{Age: 12, Name: "John"}} }
    b, err := json.Marshal(user)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(b))
}

https://play.golang.org/p/gThG4mx0ogf

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM