Populate a nested struc from a nested json

Hi, I’m new to Golang. I’m doing my first steps with it writing a simple API that calculates and returns prices and IRR of some country-specific bonds.

I have a json file with the info for every bond. This is the json example file:

[
    {
        "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}]
        }
]

Since dates are written in "2006-01-02" format, I need to parse them to time.Time

Therefore I’ve written a method to be called when unmarshaling the json in order to handle the date format.

This is the code that parses the JSON data.

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)
}

The problem that I got is that the method of the inner struct is not mapping the data into the inner struct.

See the output:

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

The Cashflow should have the 3 dates, rate, amortization and so on…

Any guess why the method is not mapping the data to the inner struct?

This is the link to a playground version of the code: Go Playground - The Go Programming Language

Thanks!

You don’t assign aux.Cashflow to u.Cashflow in the Bond UnmarshallJSON method. You should do that around line 109.

BTW, you can write (*u).Coupon as u.Coupon.

1 Like

Thanks a lot.
I didn’t know that I have to assign the inner struct as a whole, in addition, to setting up the method itself for the Flujo struct.

Ok to the (*c) → c. Is that because methods allow using pointers and receivers interchangeably?

One last. I never call the method unmarshalJSON. How is it ends running that method?

Thanks again!

You are decoding the JSON into an auxiliary struct. You need to assign its fields that you want to preserve to the receiver’s fields. It’s no different from Coupon or Ticker that way.

No, it is a syntactic convenience provided by the language for accessing fields or methods of a pointer to a struct. A Tour of Go

Eventually here:
https://cs.opensource.google/go/go/+/refs/tags/go1.18:src/encoding/json/decode.go;l=607-613

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.