【问题标题】:Converting a complex struct to a simple json将复杂的结构转换为简单的 json
【发布时间】:2021-05-01 20:38:30
【问题描述】:

背景

我有一个结构,我通过 API 作为 json 传递给用户。这行得通。

我现在正尝试发送与我的 UI 可以轻松转换为 csv 并允许用户下载的对象相同的结构。

我有在 UI 上运行的代码,可以将简单对象转换为 CSV。所以我可以发送一个值只是整数或字符串的对象,没问题。但是,我的结构具有对象和数组的值。所以,我试图找到一种方法将我的复杂 json 对象转换为更简单的对象。更简单的对象应该只包含没有引号的值(如 int)或只有一组外部引号(如字符串)。

问题

如何将 golang 结构转换为具有“简单”值(字符串、布尔值、整数但不是数组或带有更多引号的对象)的 json?

示例

例如,此代码将用户转换为具有以下格式的 json:

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

,但我想要格式

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

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

这是我的代码

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))
}

备注

  1. 我正在使用这个站点:https://play.golang.org/ 来测试上面的代码。
  2. 如果该函数通过循环遍历所有结构字段来工作,那就太好了,因为在现实生活中,我有一个包含大约 30 个字段的结构,其中一些是具有嵌套结构的结构。

【问题讨论】:

  • @Inian 是的,我只想要一个“平面”json。我相信它们是有效的 json。它们只有一个字符串值,其中字符串包含 [ 和 { 字符。
  • 您现在有文字字符串,[{ 即 JSON 的数组和对象特征不再有效。这是你需要的吗?
  • @Inian 是的,它用于 csv 文件,其中标题为“FavSnacks”,值为 [apple, orange]。如果我留在双引号中,它会破坏 excel 等上的列对齐方式。
  • 你不想要JSON,所以不要使用encoding/json
  • @EmilePels 我的目标仍然是通过 API 传递这个对象,API 发送的类型是 application/json 类型。这就是我保留 json 类型的原因。我是否应该考虑更改 API 以传递不同的类型?

标签: json go


【解决方案1】:

使用您要编组的值构建地图。使用反射 API 遍历结构中的字段并收集映射中的值。如果字段的值是切片或结构,则将该字段编码为 JSON 并在映射中使用该值。返回编码为 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

【讨论】:

    【解决方案2】:

    只需实现 Marshaler 接口 :)

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-15
      • 1970-01-01
      • 2021-05-02
      • 2018-01-25
      • 2019-12-20
      • 2021-12-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多