Golang中如何自定义时间类型进行xml、json的序列化/反序列化

在日常开发工作中,我们进行会遇到将 struct 序列化 json字符串以及将 json字符串 反序列化为 struct 的场景,大家也对此十分熟悉。

最近工作中,遇到了需要将 struct 序列化 xml字符串以及将 xml字符串 反序列化为 struct 的场景,对于普通类型的字段,比如int、string 等类型,直接使用并没有啥问题。

当遇到 时间类型 时,序列化和反序列化并不是自己想要的格式,这个时候就需要我们自定义时间类型的序列化/反序列化方式。

对于json序列化时间类型,大家可能已经比较熟悉了,一般是自定义一个时间类型或者为struct自定义MarshalJSON()([]byte, error)UnmarshalJSON(b []byte) error方法,这样就可以实现将时间格式化为我们想要的格式了。

其实对于xml来说也是一样的,方式也是上面两种,这里就介绍下自定义时间类型,来实现xml的序列化/反序列化。

代码如下:

package main

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

const timeLayout = "2006-01-02T15:04:05.000+08:00"

var location *time.Location

func init() {
	location, _ = time.LoadLocation("Asia/Shanghai")
}

// XSDDateTime is a type for representing xsd:datetime in Golang
type XSDDateTime struct {
	innerTime time.Time
}

func CreateXSDDateTime(dt time.Time) XSDDateTime {
	return XSDDateTime{
		innerTime: dt,
	}
}

func (xdt XSDDateTime) String() string {
	return xdt.innerTime.Format(timeLayout)
}

// ToGoTime converts the time to time.Time by checking if a TZ is specified.
// If there is a TZ, that TZ is used, otherwise local TZ is used
func (xdt *XSDDateTime) ToGoTime() time.Time {

	return time.Date(xdt.innerTime.Year(), xdt.innerTime.Month(), xdt.innerTime.Day(),
		xdt.innerTime.Hour(), xdt.innerTime.Minute(), xdt.innerTime.Second(),
		xdt.innerTime.Nanosecond(), location)
}

func (xdt XSDDateTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
	value := xdt.innerTime.Format(timeLayout)
	attr := xml.Attr{Name: name, Value: value}
	return attr, nil
}

func (xdt *XSDDateTime) UnmarshalXMLAttr(attr xml.Attr) error {
	value := attr.Value
	t, err := time.Parse(timeLayout, value)
	if err != nil {
		return err
	}
	xdt.innerTime = t
	return nil
}

func (xdt XSDDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	formatted := xdt.innerTime.Format(timeLayout)
	return e.EncodeElement(formatted, start)
}

func (xdt *XSDDateTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var value string
	if err := d.DecodeElement(&value, &start); err != nil {
		return err
	}

	parsedTime, err := time.Parse(timeLayout, value)
	if err != nil {
		return err
	}

	xdt.innerTime = parsedTime
	return nil
}

func (xdt XSDDateTime) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf(`"%s"`, xdt.innerTime.Format(timeLayout))), nil
}

func (xdt *XSDDateTime) UnmarshalJSON(data []byte) error {
	var dt time.Time

	if err := json.Unmarshal(data, &dt); err != nil {
		return err
	}
	xdt.innerTime = dt
	return nil
}

type Test struct {
	TD  XSDDateTime  `xml:"TD,attr"`
	TD1 *XSDDateTime `xml:"TD1,attr,omitempty"`
	T   XSDDateTime  `xml:"T"`
	T1  *XSDDateTime `xml:"T1,omitempty"`
}

func main() {

	// 创建一个 soap.XSDDateTime 类型的实例
	xsdDateTime := CreateXSDDateTime(time.Now())
	fmt.Println("now -->", time.Now())

	t := Test{
		TD: xsdDateTime,
		T:  xsdDateTime,
	}

	// 使用 xml.Marshal 将 soap.XSDDateTime 编组为 XML 数据
	xmlData, err := xml.MarshalIndent(t, "", " ")
	if err != nil {
		fmt.Println("Error marshaling:", err)
		return
	}

	// 输出编组后的 XML 数据
	fmt.Println(string(xmlData))
	fmt.Println(strings.Repeat("-", 10))

	//tt := `<Test TD="2023-11-24T10:24:27.129+08:00">
	//<T>2023-11-24T18:22:27.129+08:00</T>
	//</Test>
	//`

	tt := string(xmlData)
	var dddd Test
	err = xml.Unmarshal([]byte(tt), &dddd)
	fmt.Println(err)
	fmt.Printf("Test --> %+v\n", dddd)
	fmt.Printf("%v\n", dddd.T.ToGoTime())
	fmt.Printf("%v\n", dddd.T.ToGoTime().Format(timeLayout))

}

执行结果:

now --> 2023-11-30 11:00:54.0918059 +0800 CST m=+0.003982301
<Test TD="2023-11-30T11:00:54.091+08:00">
 <T>2023-11-30T11:00:54.091+08:00</T>
</Test>
----------
<nil>
t --> {TD:2023-11-30T11:00:54.091+08:00 TD1:<nil> T:2023-11-30T11:00:54.091+08:00 T1:<nil>}
2023-11-30 11:00:54.091 +0800 CST
2023-11-30T11:00:54.091+08:00
posted @ 2023-12-01 09:36  画个一样的我  阅读(432)  评论(0编辑  收藏  举报