Golang如何将动态JSON转换为结构体

Golang如何将动态JSON转换为结构体 我在将API返回的JSON转换为静态结构体时遇到了一些问题,以下是我的JSON示例:

{
    "layoutID": 1,
    "data": [
        {
            "widgetID": 1,
            "name": "widget 1",
            "type": "product",
            "data": [
                {
                    "productID": 1,
                    "name": "product 1"
                }
            ]
        },
        {
            "widgetID": 1,
            "name": "widget 2",
            "type": "voucher",
            "data": [
                {
                    "voucherID": 1,
                    "name": "voucher 1"
                }
            ]
        }
    ]
}

我想为这个JSON创建一个结构体,但不知道如何转换。我尝试使用接口进行转换和类型断言,但没有成功。

你能帮帮我吗?谢谢


更多关于Golang如何将动态JSON转换为结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

到目前为止你尝试了什么?它为什么没有起作用?

更多关于Golang如何将动态JSON转换为结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我认为你可以在这里找到答案:http://mattyjwilliams.blogspot.com/2013/01/using-go-to-unmarshal-json-lists-with.html 如果你还有更多问题,请告诉我们。

在你编写的循环中,Data 的类型是 widget。你不需要进行类型转换。你需要做的是遍历其中的 Data https://play.golang.org/p/QoLQxyv1zKr

https://mholt.github.io/json-to-go/ https://blog.golang.org/json

你也可以将接收对象定义为 map[string]interface{},JSON 反序列化会递归地填充所有细节。

func main() {
    fmt.Println("hello world")
}

为什么不直接反序列化到你创建的结构体中呢?

	var l Layout

	if err := json.Unmarshal(byt, &l); err != nil {
		panic(err)
	}

	fmt.Printf("layout: %+v",l)

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

谢谢 @eudore

我已经尝试过这个方法,但问题仍未解决,以下是结果:

type AutoGenerated struct {
	LayoutID int `json:"layoutID"`
	Data     []struct {
		WidgetID int    `json:"widgetID"`
		Name     string `json:"name"`
		Type     string `json:"type"`
		Data     []struct {
			ProductID int    `json:"productID"`
			Name      string `json:"name"`
		} `json:"data"`
	} `json:"data"`
}

继续讨论 尝试将动态 JSON 转换为结构体

非常感谢你的帮助,这确实帮了我大忙。但是,在反序列化之后我又遇到了另一个问题,我发现 Widget 结构体中的 data[]interface{} 类型。

我尝试转换这些代码,但结果是错误 invalid type assertion。我该怎么做,或者有其他方法可以将其转换为 Product 和 Voucher 结构体吗?

以下是我检查后的改进代码。 https://play.golang.org/p/2zmGTlbC9pB

非常感谢! 👍

我一直在创建这样的结构体

type Layout struct {
	LayoutID int      `json:"layoutID"`
	Data     []Widget `json:"data"`
}

type Widget struct {
	WidgetID int           `json:"widgetID"`
	Name     string        `json:"name"`
	Type     string        `json:"type"`
	Data     []interface{} `json:"data"`
}

type Product struct {
	ProductID int    `json:"productID"`
	Name      string `json:"name"`
}

type Voucher struct {
	VoucherID int    `json:"voucherID"`
	Name      string `json:"name"`
}

我将尝试运行并根据这个JSON创建模拟。这是我的代码 https://play.golang.org/p/M8HTKWPEHqn

当我将其转换为结构体时,结果会是false

我明白,但小部件1中的Data是产品结构体,例如我设置了ProductIDName,而小部件2中的Data不是产品结构体,而是优惠券结构体,这就是为什么我需要进行类型转换,因为Data中的内容并不相同。所以我这样设置的原因在此:https://play.golang.org/p/2zmGTlbC9pB。

而且我发现了问题,在反序列化layout之后,小部件2数据的结果是ProductID=0,而不是VoucherID=1

type Widget struct {
	WidgetID int           `json:"widgetID"`
	Name     string        `json:"name"`
	Type     string        `json:"type"`
	Data     []interface{} `json:"data"`
}

因为小部件中的Data是动态的,所以我将其类型设置为[]interface{},而不仅仅是产品结构体。

谢谢。

对于动态JSON结构,可以使用json.RawMessage配合自定义的UnmarshalJSON方法。以下是解决方案:

package main

import (
    "encoding/json"
    "fmt"
)

type Layout struct {
    LayoutID int             `json:"layoutID"`
    Data     []WidgetWrapper `json:"data"`
}

type WidgetWrapper struct {
    WidgetID int             `json:"widgetID"`
    Name     string          `json:"name"`
    Type     string          `json:"type"`
    Data     json.RawMessage `json:"data"`
}

func (w *WidgetWrapper) UnmarshalJSON(data []byte) error {
    type Alias WidgetWrapper
    aux := &struct {
        *Alias
    }{
        Alias: (*Alias)(w),
    }
    
    if err := json.Unmarshal(data, aux); err != nil {
        return err
    }
    
    switch w.Type {
    case "product":
        var products []Product
        if err := json.Unmarshal(w.Data, &products); err != nil {
            return err
        }
        w.Data = json.RawMessage(products)
    case "voucher":
        var vouchers []Voucher
        if err := json.Unmarshal(w.Data, &vouchers); err != nil {
            return err
        }
        w.Data = json.RawMessage(vouchers)
    }
    
    return nil
}

type Product struct {
    ProductID int    `json:"productID"`
    Name      string `json:"name"`
}

type Voucher struct {
    VoucherID int    `json:"voucherID"`
    Name      string `json:"name"`
}

func main() {
    jsonStr := `{
        "layoutID": 1,
        "data": [
            {
                "widgetID": 1,
                "name": "widget 1",
                "type": "product",
                "data": [
                    {
                        "productID": 1,
                        "name": "product 1"
                    }
                ]
            },
            {
                "widgetID": 1,
                "name": "widget 2",
                "type": "voucher",
                "data": [
                    {
                        "voucherID": 1,
                        "name": "voucher 1"
                    }
                ]
            }
        ]
    }`
    
    var layout Layout
    if err := json.Unmarshal([]byte(jsonStr), &layout); err != nil {
        panic(err)
    }
    
    fmt.Printf("Layout ID: %d\n", layout.LayoutID)
    for _, widget := range layout.Data {
        fmt.Printf("Widget: %s (Type: %s)\n", widget.Name, widget.Type)
    }
}

如果需要访问具体类型的数据,可以添加一个方法:

func (w *WidgetWrapper) GetData() interface{} {
    switch w.Type {
    case "product":
        var products []Product
        json.Unmarshal(w.Data, &products)
        return products
    case "voucher":
        var vouchers []Voucher
        json.Unmarshal(w.Data, &vouchers)
        return vouchers
    }
    return nil
}

这样可以在解析时根据type字段动态处理不同的数据结构。

回到顶部