Golang中如何将嵌套结构体的部分字段解析为其他格式

Golang中如何将嵌套结构体的部分字段解析为其他格式 大家好!

作为Go语言的新手,我仍在努力解决一些问题,但看到事情开始运转起来,我感到非常兴奋。

我正在处理一些包含 time.Time 字段的嵌套结构体。 我想将这些结构体序列化为JSON以保存到文件中,但我需要将这些 time.Time 字段转换为 string 类型,因为我不想要Go语言使用的冗长日期格式。 因此,我需要使用 time.Format("2006-01-02") 来转换这些字段,然后再序列化结构体。

这是我的嵌套结构体:

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
}

我是否需要将结构体转换为下面的类型,然后再用 json.Marshal 进行序列化?

type FlujoOut struct {
	Date     string
	Rate     float64
	Amort    float64
	Residual float64
	Amount   float64
}

type BondOut struct {
	ID        string
	Ticker    string
	IssueDate string
	Maturity  string
	Coupon    float64
	Cashflow  []FlujoOut
}

如果是这样,考虑到内部包含一个结构体切片,我该如何将一个结构体转换为另一个结构体呢?

先谢谢了!!


更多关于Golang中如何将嵌套结构体的部分字段解析为其他格式的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

尝试为 DateType 提供一个 String() 方法,该方法可以按照你喜欢的格式进行格式化。

更多关于Golang中如何将嵌套结构体的部分字段解析为其他格式的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


考虑以下方案如何:

type Date time.Time
func (d Date) MarshalJSON() ([]byte,error) {
  return []byte(time.Time(d).Format("2006-01-0")),nil
}

然后将你 FlujoBond 结构体中的所有 time.Time 字段都声明为 Date 类型?

你可能还需要一个 UnmarshalJSON 方法。

如果你为 *Date 添加一个 UnmarshalJSON 方法,那么 *Flujo*Bond 就不需要 UnmarshalJSON 方法了。你现有的 UnmarshalJSON 方法只是为了解析日期文本,但如果字段是 Date 类型,这个解析工作应该自动为你完成。

Go Playground - The Go Programming Language

Go 是一种开源编程语言,它使得构建简单、可靠且高效的软件变得容易。

首先,非常感谢您的帮助!

现在我觉得我开始理解正确了。

在结构体中使用自定义类型时,日期以 wall: ext: loc: 的格式存储。当打印或保存它们时,我应该使用 time.Time(var_Date).Format("2006-01-02") 来格式化它们。

为什么使用自定义类型时,值会以那种方式存储?Go Playground - The Go Programming Language

如何打印整个结构体并让日期格式化为 "2006-01-02"

我从一个包含嵌套结构体的JSON文件开始,但由于该文件是由另一个(外部)模块填充的,其中的日期格式为"2006-01-02"

以下是一个示例:

{
    "ID": "1",
    "ticker": "S30J2",
    "issueDate": "2022-01-31",
    "maturity": "2023-06-30",
    "coupon": 0,
    "cashFlow": [
        {   "date": "2022-06-30",
            "rate": 0,
            "amortization": 1,
            "residual": 0,
            "amount": 50},
            {
            "date": "2023-06-30",
            "rate": 0,
            "amortization": 1,
            "residual": 0,
            "amount": 50}
    ]
}

我使用以下UnmarshalJSON方法将它们解组到相应的结构体(Bond 和 Flujo)中。

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
}

现在,我已经将JSON加载到结构体中。

当我尝试编写的API通过POST端点接收到一个新项(需要添加到结构体包含的项目中)时,它接收到的日期格式是"2006-01-02"。因此,我将其添加到结构体中,并使用上述UnmarshalJSON方法进行解析。

之后,我需要将结构体转储到文件中以进行保存。但是,我在结构体中将日期作为time.Time类型处理,但需要再次以"2006-01-02"格式将它们写入文件。

如果我按照您提到的方式将所有结构体中的Date字段都定义为time.Time,那么UnmarshalJSON方法将无法工作。

如果我没能表达得更清楚,我表示歉意。

在Go语言中,你可以通过实现自定义的JSON序列化方法来解决这个问题,而不需要创建额外的输出结构体。具体来说,你可以为time.Time类型实现MarshalJSON方法,或者为你的结构体实现自定义的JSON标签。

以下是两种解决方案:

方案1:为结构体字段添加JSON标签

你可以为结构体字段添加JSON标签,指定序列化时的格式。这需要你为每个包含time.Time字段的结构体实现MarshalJSON方法。

首先,为FlujoBond结构体实现MarshalJSON方法:

import (
    "encoding/json"
    "time"
)

type Flujo struct {
    Date     time.Time `json:"date"`
    Rate     float64   `json:"rate"`
    Amort    float64   `json:"amort"`
    Residual float64   `json:"residual"`
    Amount   float64   `json:"amount"`
}

func (f Flujo) MarshalJSON() ([]byte, error) {
    type Alias Flujo
    return json.Marshal(&struct {
        Date string `json:"date"`
        *Alias
    }{
        Date:  f.Date.Format("2006-01-02"),
        Alias: (*Alias)(&f),
    })
}

type Bond struct {
    ID        string    `json:"id"`
    Ticker    string    `json:"ticker"`
    IssueDate time.Time `json:"issueDate"`
    Maturity  time.Time `json:"maturity"`
    Coupon    float64   `json:"coupon"`
    Cashflow  []Flujo   `json:"cashflow"`
}

func (b Bond) MarshalJSON() ([]byte, error) {
    type Alias Bond
    return json.Marshal(&struct {
        IssueDate string `json:"issueDate"`
        Maturity  string `json:"maturity"`
        *Alias
    }{
        IssueDate: b.IssueDate.Format("2006-01-02"),
        Maturity:  b.Maturity.Format("2006-01-02"),
        Alias:     (*Alias)(&b),
    })
}

然后,你可以直接使用json.Marshal序列化Bond结构体:

bond := Bond{
    ID:        "bond001",
    Ticker:    "BND",
    IssueDate: time.Now(),
    Maturity:  time.Now().AddDate(5, 0, 0),
    Coupon:    5.0,
    Cashflow: []Flujo{
        {Date: time.Now(), Rate: 1.0, Amort: 1000, Residual: 9000, Amount: 10000},
    },
}

jsonData, err := json.Marshal(bond)
if err != nil {
    panic(err)
}
fmt.Println(string(jsonData))

方案2:使用自定义类型

另一种方法是定义一个自定义的Time类型,为其实现MarshalJSON方法,然后在结构体中使用这个自定义类型。

首先,定义自定义的Time类型:

type JSONDate time.Time

func (j JSONDate) MarshalJSON() ([]byte, error) {
    return []byte(`"` + time.Time(j).Format("2006-01-02") + `"`), nil
}

func (j *JSONDate) UnmarshalJSON(data []byte) error {
    t, err := time.Parse(`"2006-01-02"`, string(data))
    if err != nil {
        return err
    }
    *j = JSONDate(t)
    return nil
}

然后,在结构体中使用JSONDate类型:

type Flujo struct {
    Date     JSONDate `json:"date"`
    Rate     float64  `json:"rate"`
    Amort    float64  `json:"amort"`
    Residual float64  `json:"residual"`
    Amount   float64  `json:"amount"`
}

type Bond struct {
    ID        string   `json:"id"`
    Ticker    string   `json:"ticker"`
    IssueDate JSONDate `json:"issueDate"`
    Maturity  JSONDate `json:"maturity"`
    Coupon    float64  `json:"coupon"`
    Cashflow  []Flujo  `json:"cashflow"`
}

现在,你可以直接序列化Bond结构体,日期字段会自动格式化为字符串:

bond := Bond{
    ID:        "bond001",
    Ticker:    "BND",
    IssueDate: JSONDate(time.Now()),
    Maturity:  JSONDate(time.Now().AddDate(5, 0, 0)),
    Coupon:    5.0,
    Cashflow: []Flujo{
        {Date: JSONDate(time.Now()), Rate: 1.0, Amort: 1000, Residual: 9000, Amount: 10000},
    },
}

jsonData, err := json.Marshal(bond)
if err != nil {
    panic(err)
}
fmt.Println(string(jsonData))

这两种方案都可以避免创建额外的输出结构体,并允许你直接序列化原始结构体,同时将time.Time字段转换为指定格式的字符串。选择哪种方案取决于你的具体需求和个人偏好。

回到顶部