【问题标题】:How to make interface slice and append value [closed]如何制作接口切片和附加值[关闭]
【发布时间】:2021-08-26 14:12:44
【问题描述】:

我想做一个函数来解析字符串数组中的切片(一种抽象解析方法,可以是 JSON、msgpack 或 XML)。这个 slice 的类型不确定,可以是 [] A 或者 [] * A。但是现在我有一个问题。你能帮帮我吗

func unmarshalSlice(strings []string, to interface{}) (err error) {
    elementType := reflect.TypeOf(to).Elem()
    to = reflect.MakeSlice(reflect.SliceOf(elementType), 0, len(strings))
    for _, str := range strings {
        value := reflect.New(elementType).Interface()
        if err = json.Unmarshal([]byte(str), value); nil != err {
            return
        }
        to = reflect.Append(to.(reflect.Value), reflect.ValueOf(value).Elem())
    }
    

    return
}

测试代码是

func TestUnmarshalSlice(t *testing.T) {
    var members []*Member
    _ = unmarshalSlice([]string{
        `{
"Value": "store"
}`, `{
"Value": "zhang"
}`,
    }, members)
    fmt.Println(members)
}

【问题讨论】:

  • “但现在我有一个问题”这是什么?
  • 变量为 nil
  • 不清楚您要做什么。您是否只是想获取一段 JSON 字符串并将它们解组为一段结构,但使用空接口以“通用”方式?
  • 什么是Member
  • 如果输入类型是[]*whatever,这个value := reflect.New(elementType).Interface()会产生一个**whatever见reflect.Indirect cs.opensource.google/go/go/+/refs/tags/go1.17:src/reflect/…

标签: go generics reflect


【解决方案1】:

我已更正了您的 unmarshalSlice 函数中的一些行。

首先,您必须创建已发送类型的切片,然后将这些类型值附加到该切片。最后将该切片分配为已发送的可分配接口的值。

并使用可分配类型调用unmarshalSlice 函数。

type Member struct {
    Value string
}
func TestUnmarshalSlice(t *testing.T) {
    var members []Member
    _ = unmarshalSlice([]string{
        `{
"Value": "store"
}`, `{
"Value": "zhang"
}`,
    }, &members)
    fmt.Println(members[0], members[1]) //Output: {store} {zhang}
}

func TestUnmarshalPointerSlice(t *testing.T) {
    var members []*Member
    _ = unmarshalSlice([]string{
        `{
"Value": "store"
}`, `{
"Value": "zhang"
}`,
    }, &members)
    fmt.Println(members[0], members[1]) //Output: &{store} &{zhang}
}

func unmarshalSlice(strings []string, to interface{}) (err error) {
    elementType := reflect.TypeOf(to).Elem().Elem()
    // create slice of *Member{}
    o := reflect.MakeSlice(reflect.TypeOf(to).Elem(), 0, len(strings))
    for _, str := range strings {
        // create empty assignable Member{}
        value := reflect.New(elementType).Interface()
        if err = json.Unmarshal([]byte(str), value); nil != err {
            return
        }
        // append *Member{} to slice
        o = reflect.Append(o, reflect.ValueOf(value).Elem())
    }

    // set created slice to the value of "to"
    reflect.ValueOf(to).Elem().Set(o)

    return
}

你可以运行测试here

【讨论】:

  • 总是写一个测试函数play.golang.org/p/E11YXJUmea0
  • 抱歉,还在更新。我会更新
  • @mh-cbon,已更新完整代码
  • 它又坏了。很抱歉这么糟糕。 play.golang.org/p/w1qrpS-6ZIr 你考虑过 reflect.Indirect 吗?但它不存在于 reflect.Type 类型。
  • 是的,我评论很慢,我的意思是以前的版本。最后一个很棒。
【解决方案2】:

我不相信您期望从该功能中获得的灵活性是一件好事...

无论如何,很快(当泛型被添加到 Go 1.18 中的语言中时),您将能够编写一个签名稍有不同的泛型函数,它可以满足您的需求无需借助反射

package main

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

type Member struct {
    Value string
}

func unmarshalSlice[T any](ss []string) ([]T, error) {
    ts := make([]T, 0, len(ss))
    for _, str := range ss {
        var t T
        if err := json.Unmarshal([]byte(str), &t); err != nil {
            return nil, err
        }
        ts = append(ts, t)
    }
    return ts, nil
}

func main() {
    input := []string{
        `{"Value": "store"}`,
        `{"Value": "zhang"}`,
    }

    members, err := unmarshalSlice[Member](input)
    if err != nil {
        fmt.Fprint(os.Stderr, err)
        os.Exit(1)
    }
    fmt.Println(members)

    pmembers, err := unmarshalSlice[*Member](input)
    if err != nil {
        fmt.Fprint(os.Stderr, err)
        os.Exit(1)
    }
    for _, m := range pmembers {
        fmt.Println(*m)
    }
}

输出:

[{store} {zhang}]
{store}
{zhang}

(Playground))

【讨论】:

  • 如果这个方法需要绑定一个struct,怎么写?我试过了,编译器会报错
  • @user16759991 类型参数必须来自相关的(通用)结构类型。方法本身不能引入新的类型参数。
【解决方案3】:

您不需要那种复杂的代码。看看这个非常简单的解决方案

package main

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

type Member struct {
    Value string
}

func main() {
    inputs := []string{
        `{
"Value": "store"
}`, `{
"Value": "zhang"
}`,
    }
    var members []Member
    s := strings.Join(inputs, ",")
    s = strings.TrimSuffix(s, ",")
    s = fmt.Sprintf("[%v]", s)
    json.Unmarshal([]byte(s), &members)
    fmt.Println(members[0], members[1])

    var out []*Member
    json.Unmarshal([]byte(s), &out)
    fmt.Println(out[0], out[1])
}

【讨论】:

    猜你喜欢
    • 2017-09-22
    • 2020-12-31
    • 2019-10-19
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-20
    相关资源
    最近更新 更多