Golang中如何从嵌套JSON解析并填充嵌套结构体

Golang中如何从嵌套JSON解析并填充嵌套结构体 大家好,我是Go语言的新手。我正在通过编写一个简单的API来学习它,这个API用于计算并返回一些特定国家债券的价格和内部收益率。

我有一个包含每只债券信息的JSON文件。这是一个JSON示例文件:

[
    {
        "ID": "1",
        "ticker": "GD30",
        "issueDate": "2020-01-01",
        "maturity": "2030-07-09",
        "coupon": 0.05,
        "cashFlow": [
            {   "date": "2021-01-09",
                "rate": 0.00125,
                "amortization": 0,
                "residual": 1,
                "amount": 0},
            {   "date": "2021-07-09",
                "rate": 0.00125,
                "amortization": 0,
                "residual": 1,
                "amount": 0},
            {   "date": "2022-01-09",
                "rate": 0.005,
                "amortization": 0,
                "residual": 1,
                "amount": 0}]
        }
]

由于日期是以 "2006-01-02" 格式书写的,我需要将它们解析为 time.Time 类型。

因此,我编写了一个在JSON反序列化时调用的方法,以处理日期格式。

这是解析JSON数据的代码:

package main

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

const data = `[
    {
        "ID": "1",
        "ticker": "GD30",
        "issueDate": "2020-01-01",
        "maturity": "2030-07-09",
        "coupon": 0.05,
        "cashFlow": [
            {   "date": "2021-01-09",
                "rate": 0.00125,
                "amortization": 0,
                "residual": 1,
                "amount": 0},
            {   "date": "2021-07-09",
                "rate": 0.00125,
                "amortization": 0,
                "residual": 1,
                "amount": 0},
            {   "date": "2022-01-09",
                "rate": 0.005,
                "amortization": 0,
                "residual": 1,
                "amount": 0}]
        }
]`

// define data structure to hold the json data
type Flujo struct {
	Date     time.Time
	Rate     float64
	Amort    float64
	Residual float64
	Amount   float64
}

type Bond struct {
	ID        string
	Ticker    string
	IssueDate time.Time
	Maturity  time.Time
	Coupon    float64
	Cashflow  []Flujo
}

const dateFormat = "2006-01-02"

var Bonds []Bond

func (c *Flujo) UnmarshalJSON(p []byte) error {
	var aux struct {
		Date     string  `json:"date"`
		Rate     float64 `json:"rate"`
		Amort    float64 `json:"amortization"`
		Residual float64 `json:"residual"`
		Amount   float64 `json:"amount"`
	}
	err := json.Unmarshal(p, &aux)
	if err != nil {
		return err
	}

	t, err := time.Parse(dateFormat, aux.Date)
	if err != nil {
		return err
	}
	(*c).Date = t
	(*c).Rate = aux.Rate
	(*c).Amort = aux.Amort
	(*c).Residual = aux.Residual
	(*c).Amount = aux.Amount
	return nil
}

func (u *Bond) UnmarshalJSON(p []byte) error {
	var aux struct {
		ID        string  `json:"id"`
		Ticker    string  `json:"ticker"`
		IssueDate string  `json:"issueDate"`
		Maturity  string  `json:"maturity"`
		Coupon    float64 `json:"coupon"`
		Cashflow  []Flujo `json:"cashFlow"`
	}

	err := json.Unmarshal(p, &aux)
	if err != nil {
		return err
	}

	t, err := time.Parse(dateFormat, aux.IssueDate)
	if err != nil {
		return err
	}
	y, err := time.Parse(dateFormat, aux.Maturity)
	if err != nil {
		return err
	}
	(*u).ID = aux.ID
	(*u).Ticker = aux.Ticker
	(*u).IssueDate = t
	(*u).Maturity = y
	(*u).Coupon = aux.Coupon
	return nil
}

func main() {
	// json data
	// unmarshall the JSON
	err := json.Unmarshal([]byte(data), &Bonds)
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Println("Bonds: ", Bonds)
	fmt.Println("Cashflow: ", Bonds[0].Cashflow)
}

我遇到的问题在于,内部结构体的方法没有将数据映射到内部结构体中。

查看输出:

Bonds:  [{1 GD30 2020-01-01 00:00:00 +0000 UTC 2030-07-09 00:00:00 +0000 UTC 0.05 []}]
Cashflow:  []

Cashflow 本应包含那3个日期、利率、摊销等信息……

有谁知道为什么这个方法没有将数据映射到内部结构体吗?

这是代码在Go Playground上的链接:Go Playground - The Go Programming Language

谢谢!


更多关于Golang中如何从嵌套JSON解析并填充嵌套结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

Bond 类型的 UnmarshallJSON 方法中,你没有将 aux.Cashflow 赋值给 u.Cashflow。你应该在第109行附近完成这个赋值。

顺便提一下,你可以将 (*u).Coupon 写成 u.Coupon

更多关于Golang中如何从嵌套JSON解析并填充嵌套结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢。 我之前不知道,除了为 Flujo 结构体设置方法本身之外,我还需要将内部结构体作为一个整体进行赋值。

好的,关于 (*c) → c。这是因为方法允许指针和接收器互换使用吗?

最后一个问题。我从未调用过 unmarshalJSON 方法。它最终是如何运行那个方法的呢?

再次感谢!

Juan_Truffa:

将内部结构体作为一个整体赋值

您正在将JSON解码到一个辅助结构体中。您需要将其想要保留的字段赋值给接收者的字段。这与处理Coupon或Ticker的方式没有区别。

Juan_Truffa:

这是因为方法允许指针和接收器互换使用吗?

不,这是语言提供的一种语法便利,用于访问指向结构体的指针的字段或方法。Go语言之旅

Juan_Truffa:

调用方法 unmarshalJSON

最终在这里: https://cs.opensource.google/go/go/+/refs/tags/go1.18:src/encoding/json/decode.go;l=607-613

BondUnmarshalJSON 方法中,你忘记将 aux.Cashflow 赋值给 (*u).Cashflow。这是导致 Cashflow 为空切片的原因。

以下是修正后的 Bond.UnmarshalJSON 方法:

func (u *Bond) UnmarshalJSON(p []byte) error {
	var aux struct {
		ID        string  `json:"ID"`
		Ticker    string  `json:"ticker"`
		IssueDate string  `json:"issueDate"`
		Maturity  string  `json:"maturity"`
		Coupon    float64 `json:"coupon"`
		Cashflow  []Flujo `json:"cashFlow"`
	}

	err := json.Unmarshal(p, &aux)
	if err != nil {
		return err
	}

	t, err := time.Parse(dateFormat, aux.IssueDate)
	if err != nil {
		return err
	}
	y, err := time.Parse(dateFormat, aux.Maturity)
	if err != nil {
		return err
	}
	(*u).ID = aux.ID
	(*u).Ticker = aux.Ticker
	(*u).IssueDate = t
	(*u).Maturity = y
	(*u).Coupon = aux.Coupon
	(*u).Cashflow = aux.Cashflow // 添加这一行
	return nil
}

另外,JSON 字段标签中的 "id" 应该改为 "ID" 以匹配 JSON 数据中的大写键名。修正后的完整示例如下:

package main

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

const data = `[
    {
        "ID": "1",
        "ticker": "GD30",
        "issueDate": "2020-01-01",
        "maturity": "2030-07-09",
        "coupon": 0.05,
        "cashFlow": [
            {   "date": "2021-01-09",
                "rate": 0.00125,
                "amortization": 0,
                "residual": 1,
                "amount": 0},
            {   "date": "2021-07-09",
                "rate": 0.00125,
                "amortization": 0,
                "residual": 1,
                "amount": 0},
            {   "date": "2022-01-09",
                "rate": 0.005,
                "amortization": 0,
                "residual": 1,
                "amount": 0}]
        }
]`

type Flujo struct {
	Date     time.Time
	Rate     float64
	Amort    float64
	Residual float64
	Amount   float64
}

type Bond struct {
	ID        string
	Ticker    string
	IssueDate time.Time
	Maturity  time.Time
	Coupon    float64
	Cashflow  []Flujo
}

const dateFormat = "2006-01-02"

var Bonds []Bond

func (c *Flujo) UnmarshalJSON(p []byte) error {
	var aux struct {
		Date     string  `json:"date"`
		Rate     float64 `json:"rate"`
		Amort    float64 `json:"amortization"`
		Residual float64 `json:"residual"`
		Amount   float64 `json:"amount"`
	}
	err := json.Unmarshal(p, &aux)
	if err != nil {
		return err
	}

	t, err := time.Parse(dateFormat, aux.Date)
	if err != nil {
		return err
	}
	c.Date = t
	c.Rate = aux.Rate
	c.Amort = aux.Amort
	c.Residual = aux.Residual
	c.Amount = aux.Amount
	return nil
}

func (u *Bond) UnmarshalJSON(p []byte) error {
	var aux struct {
		ID        string  `json:"ID"`
		Ticker    string  `json:"ticker"`
		IssueDate string  `json:"issueDate"`
		Maturity  string  `json:"maturity"`
		Coupon    float64 `json:"coupon"`
		Cashflow  []Flujo `json:"cashFlow"`
	}

	err := json.Unmarshal(p, &aux)
	if err != nil {
		return err
	}

	t, err := time.Parse(dateFormat, aux.IssueDate)
	if err != nil {
		return err
	}
	y, err := time.Parse(dateFormat, aux.Maturity)
	if err != nil {
		return err
	}
	u.ID = aux.ID
	u.Ticker = aux.Ticker
	u.IssueDate = t
	u.Maturity = y
	u.Coupon = aux.Coupon
	u.Cashflow = aux.Cashflow
	return nil
}

func main() {
	err := json.Unmarshal([]byte(data), &Bonds)
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Printf("Bonds: %+v\n", Bonds)
	fmt.Printf("Cashflow: %+v\n", Bonds[0].Cashflow)
}

现在输出会正确显示嵌套的 Cashflow 数据:

Bonds: [{ID:1 Ticker:GD30 IssueDate:2020-01-01 00:00:00 +0000 UTC Maturity:2030-07-09 00:00:00 +0000 UTC Coupon:0.05 Cashflow:[{Date:2021-01-09 00:00:00 +0000 UTC Rate:0.00125 Amort:0 Residual:1 Amount:0} {Date:2021-07-09 00:00:00 +0000 UTC Rate:0.00125 Amort:0 Residual:1 Amount:0} {Date:2022-01-09 00:00:00 +0000 UTC Rate:0.005 Amort:0 Residual:1 Amount:0}]}]
Cashflow: [{Date:2021-01-09 00:00:00 +0000 UTC Rate:0.00125 Amort:0 Residual:1 Amount:0} {Date:2021-07-09 00:00:00 +0000 UTC Rate:0.00125 Amort:0 Residual:1 Amount:0} {Date:2022-01-09 00:00:00 +0000 UTC Rate:0.005 Amort:0 Residual:1 Amount:0}]
回到顶部