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
相关推荐
modelmd15 分钟前
Go 编程语言指南 练习题目分享
开发语言·学习·golang
福大大架构师每日一题4 小时前
2026年1月TIOBE编程语言排行榜,Go语言排名第16,Rust语言排名13。C# 当选 2025 年度编程语言。
golang·rust·c#
拔剑纵狂歌7 小时前
helm-cli安装资源时序报错问题问题
后端·docker·云原生·容器·golang·kubernetes·腾讯云
bing.shao8 小时前
AI在电商上架图片领域的应用
开发语言·人工智能·golang
源代码•宸9 小时前
Leetcode—712. 两个字符串的最小ASCII删除和【中等】
开发语言·后端·算法·leetcode·职场和发展·golang·dp
源代码•宸9 小时前
Golang语法进阶(Context)
开发语言·后端·算法·golang·context·withvalue·withcancel
源代码•宸9 小时前
Golang语法进阶(Sync、Select)
开发语言·经验分享·后端·算法·golang·select·pool
IT=>小脑虎21 小时前
Go语言零基础小白学习知识点【基础版详解】
开发语言·后端·学习·golang
源代码•宸21 小时前
Golang语法进阶(并发概述、Goroutine、Channel)
服务器·开发语言·后端·算法·golang·channel·goroutine
WayneJoon.H1 天前
2023CISCN go_session
网络安全·golang·ctf·代码审计·ciscn