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
在 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
在 Bond 的 UnmarshalJSON 方法中,你忘记将 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}]

