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

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

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

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

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

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

代码如下:

go 复制代码
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
相关推荐
microrain11 小时前
同一个寄存器,四种读法:SagooIoT Modbus 驱动的字节序、宽度与功能码
物联网·golang·开源·sagooiot
JWASX12 小时前
Java 转 go 学习 - 并发编程(2)
学习·golang
ttwuai14 小时前
Go开源后台管理系统推荐:README、源码和License应该怎么验
开发语言·golang·开源
JWASX14 小时前
Java 转 go 学习 - 并发编程(1)
学习·golang
JWASX16 小时前
Java 转 go 学习 - 接口
学习·golang
小小龙学IT16 小时前
Go 语言 reflect 反射包深度解析:从 Type/Value 到三大定律与工业实践
前端·golang
JWASX16 小时前
Java 转 go 学习 - 并发编程(3)
学习·golang
未来之窗软件服务1 天前
Go 利器 GoReSym:剥离符号恢复 Go 二进制元数据—东方仙盟
开发语言·golang·仙盟创梦ide·东方仙盟
念何架构之路2 天前
zap WriteSyncer与Sink体系
云原生·golang
microrain2 天前
权限改了,接口立刻生效,菜单要等重新登录:SagooIoT 权限体系的四道闸门
物联网·golang·开源·sagooiot