【问题标题】:Transform struct to slice struct将结构转换为切片结构
【发布时间】:2020-09-12 02:37:39
【问题描述】:

我正在尝试通过字符串输入选择一个结构,然后根据返回的 JSON 对象或数组,解组 JSON。想办法将结构反映到切片结构上是否正确?如果是这样,如何通过反射来做到这一点? 问候, 彼得

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
)

type NameStruct struct {
    Name string
}

func main() {

    jsonData := []byte(`[{"name":"james"},{"name":"steven"}]`)
    returnModel := InitializeModel("NameStruct", jsonData)
    fmt.Println(returnModel)

    jsonData = []byte(`{"name":"james"}`)
    returnModel = InitializeModel("NameStruct", jsonData)
    fmt.Println(returnModel)

}

func getModelByName(modelType string) interface{} {
    modelMap := make(map[string]interface{})
    modelMap["NameStruct"] = new(NameStruct)

    //don't want to do this
    modelMap["arrNameStruct"] = new([]NameStruct)
    return modelMap[modelType]
}

func InitializeModel(modelName string, jsonData []byte) interface{} {
    switch IsArray(jsonData) {
    case true:
        // some conversion here, how?
        returnModel := getModelByName("NameStruct")
        if err := json.Unmarshal(jsonData, &returnModel); err != nil {
            log.Println(err)
        }
        return returnModel
    case false:
        returnModel := getModelByName("NameStruct")
        if err := json.Unmarshal(jsonData, &returnModel); err != nil {
            log.Println(err)
        }
        return returnModel
    }
    return nil
}

func IsArray(jsonData []byte) bool {
    return (bytes.HasPrefix(jsonData, []byte("["))) && (bytes.HasSuffix(jsonData, []byte("]")))
}

【问题讨论】:

  • 这似乎不是一个好主意,您是否正在尝试解决某个特定问题?例如,在您的代码中,与使用该语言提供的初始化原语相比,具体的优势是什么?例如play.golang.com/p/JlSdzo7uyN_L
  • 我有大约 50 个结构。这将在“getModelByName”函数中给我 100 行代码。我认为有更好的方法....我正在尝试解决有时我得到一个对象 json 的问题,有时我得到一个指向同一个结构的数组 json 来解组。
  • 如果你真的需要对返回的returnModel 做一些有意义的事情,你必须对值进行类型断言,在我看来,这完全违背了getModelByName 的目的。我可以看到 getModelByName 方法只有在您不太关心底层类型并且只需要将数据传递到某个地方(以另一种格式)的情况下才有意义,例如,例如,将 json 转换为 xml。
  • 我想要完成的是,我需要解组大约 50 个 api 端点。例如1 个端点可以返回一个带有字段的 json 对象,并且同一端点有时可以返回一个具有相同对象字段的数组。因此,如果它是一个对象,我可以将它解组到 NameStruct 中,如果它是一个数组到 []NameStruct 但我不想声明 2 个结构,因为它们拥有相同的字段。
  • @mkopriva 啊 - 认为可以在包级别执行此操作 - 看起来只有从特定结构值或其本机字段开始时才有可能。我想 Register 函数将是实现这一目标的最简单的方法。

标签: go struct interface reflect


【解决方案1】:

扩展我的评论,您可以创建一个Factory,其中注册了预定义的类型:

type Factory struct {
    m map[string]reflect.Type
}

func (f *Factory) Register(v interface{}) {
    vt := reflect.TypeOf(v)
    n := vt.Name()
    f.m[n] = vt
    f.m["[]"+n] = reflect.SliceOf(vt) // implicitly register a slice of type too
}

这些类型可以在运行时按名称查找并使用JSON 数据进行初始化:

func (f *Factory) Make(k string, bs []byte) (interface{}, error) {
    vt, ok := f.m[k]
    if !ok {
        return nil, fmt.Errorf("type %q not registered", k)
    }

    pv := reflect.New(vt).Interface()

    err := json.Unmarshal(bs, pv)
    if err != nil {
        return nil, err
    }

    return pv, nil
}

使用方法:

type Place struct {
    City string `json:"city"`
}

factory.Register(Place{})

p, err := factory.Make("Place", []byte(`{"city":"NYC"}`))

fmt.Printf("%#v\n", p) // &main.Place{City:"NYC"}

切片也可以:

ps, err := factory.Make("[]Place", []byte(`[{"city":"NYC"},{"city":"Dublin"}]`))

fmt.Printf("%#v\n", p, p) // &[]main.Place{main.Place{City:"NYC"}, main.Place{City:"Dublin"}}

游乐场:https://play.golang.org/p/qWEdwk-YUug

【讨论】:

  • 更新答案以隐式支持已注册类型的切片。
猜你喜欢
  • 2012-02-25
  • 2019-03-09
  • 2018-01-04
  • 2015-05-11
  • 2017-02-19
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
相关资源
最近更新 更多