使用接口解组结构切片

发布于 2025-01-16 14:42:11 字数 268 浏览 2 评论 0原文

我有一个非常有趣的案例,假设我们有这个结构体,

type Test struct {
 Field1 string `json:"field1"`
 Field2 ABC `json:"abc"`
}

type ABC interface {
  Rest()
}

解组这个结构体不是问题,您只需指向实现接口的正确结构体,除非您有 []Test

是否有当其中一个字段是接口时解组结构切片的方法?

谢谢

I have a very interesting case, let's say we have this struct

type Test struct {
 Field1 string `json:"field1"`
 Field2 ABC `json:"abc"`
}

type ABC interface {
  Rest()
}

Unmarshalling this struct is not a problem, you could just point to the right struct which implements the interface, unless you have []Test

Is there a way to unmarshall slice of structs when one of the field is interface?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

苏别ゝ 2025-01-23 14:42:11

你需要实现Unmarshaler接口来测试,

然后在UnmarshalJSON函数中你需要一一检查它(示例中的第45-55行)

幸运的是现在有了通用,这是示例:

package main

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

type Test struct {
    Field1 string `json:"field1"`
    Field2 ABC    `json:"abc"`
}

type ABCImplements interface {
    A | B
}

func UnmarshalABC[T ABCImplements](b []byte) (T, error) {
    var x T
    err := json.Unmarshal(b, &x)
    if err != nil {
        return x, err
    }

    rv := reflect.ValueOf(x)
    if rv.IsZero() {
        return x, fmt.Errorf("T is zero value")
    }

    return x, nil
}

func (m *Test) UnmarshalJSON(data []byte) error {
    temp := make(map[string]interface{}, 2)
    err := json.Unmarshal(data, &temp)
    if err != nil {
        return err
    }
    m.Field1 = temp["field1"].(string)
    b, err := json.Marshal(temp["abc"])
    if err != nil {
        return err
    }

    xB, err := UnmarshalABC[B](b)
    if err == nil {
        m.Field2 = xB
        return nil
    }

    xA, err := UnmarshalABC[A](b)
    if err == nil {
        m.Field2 = &xA
        return nil
    }

    return nil
}

type A struct {
    B string `json:"b"`
}

func (a *A) Rest() {
    fmt.Println(a.B)
}

type B struct {
    C string `json:"c"`
}

func (b B) Rest() {
    fmt.Println(b.C)
}

type ABC interface {
    Rest()
}

func main() {
    a := &A{"coba"}
    t := Test{"oke", a}

    arrT := []Test{t, t, t}

    b, err := json.Marshal(arrT)
    if err != nil {
        panic(err)
    }

    var xT []Test
    err = json.Unmarshal(b, &xT)

    fmt.Printf("%#v\n", xT)
    fmt.Println(xT[1].Field2)
}

游乐场

You need to implement Unmarshaler interface to Test,

Then in UnmarshalJSON func you need to check it one by one (line 45-55 in the example)

Luckily there is now generic, this is the example :

package main

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

type Test struct {
    Field1 string `json:"field1"`
    Field2 ABC    `json:"abc"`
}

type ABCImplements interface {
    A | B
}

func UnmarshalABC[T ABCImplements](b []byte) (T, error) {
    var x T
    err := json.Unmarshal(b, &x)
    if err != nil {
        return x, err
    }

    rv := reflect.ValueOf(x)
    if rv.IsZero() {
        return x, fmt.Errorf("T is zero value")
    }

    return x, nil
}

func (m *Test) UnmarshalJSON(data []byte) error {
    temp := make(map[string]interface{}, 2)
    err := json.Unmarshal(data, &temp)
    if err != nil {
        return err
    }
    m.Field1 = temp["field1"].(string)
    b, err := json.Marshal(temp["abc"])
    if err != nil {
        return err
    }

    xB, err := UnmarshalABC[B](b)
    if err == nil {
        m.Field2 = xB
        return nil
    }

    xA, err := UnmarshalABC[A](b)
    if err == nil {
        m.Field2 = &xA
        return nil
    }

    return nil
}

type A struct {
    B string `json:"b"`
}

func (a *A) Rest() {
    fmt.Println(a.B)
}

type B struct {
    C string `json:"c"`
}

func (b B) Rest() {
    fmt.Println(b.C)
}

type ABC interface {
    Rest()
}

func main() {
    a := &A{"coba"}
    t := Test{"oke", a}

    arrT := []Test{t, t, t}

    b, err := json.Marshal(arrT)
    if err != nil {
        panic(err)
    }

    var xT []Test
    err = json.Unmarshal(b, &xT)

    fmt.Printf("%#v\n", xT)
    fmt.Println(xT[1].Field2)
}

playground

耀眼的星火 2025-01-23 14:42:11

使用以下代码将接口字段解组为特定的具体类型。有关更多详细信息,请参阅评论:

// Example is the concrete type.
type Example struct{ Hello string }
func (e *Example) Rest() {}

// Implement the json.Unmarshaler interface to control how
// values of type Test are unmarsheled.  
func (m *Test) UnmarshalJSON(data []byte) error {
    // Setup fields as needed.
    m.Field2 = &Example{}

    // We cannot call json.Unmarshal(data, m) to do the work
    // because the json.Unmarshal will recurse back to this 
    // function. To prevent the recursion, we declare a new 
    // type with the same field layout as Test, but no methods:
    type t Test

    // Convert receiver m to a t* and unmarshal using that pointer.
    v := (*t)(m)

    return json.Unmarshal(data, v)
}

在 Playground 上运行代码

Use the following code to Unmarshal the interface field to a specific concrete type. See the commentary for more details:

// Example is the concrete type.
type Example struct{ Hello string }
func (e *Example) Rest() {}

// Implement the json.Unmarshaler interface to control how
// values of type Test are unmarsheled.  
func (m *Test) UnmarshalJSON(data []byte) error {
    // Setup fields as needed.
    m.Field2 = &Example{}

    // We cannot call json.Unmarshal(data, m) to do the work
    // because the json.Unmarshal will recurse back to this 
    // function. To prevent the recursion, we declare a new 
    // type with the same field layout as Test, but no methods:
    type t Test

    // Convert receiver m to a t* and unmarshal using that pointer.
    v := (*t)(m)

    return json.Unmarshal(data, v)
}

Run the code on the playground.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文