英文:
Golang XML Unmarshal and time.Time fields
问题
我有通过REST API检索到的XML数据,我正在将其解组为GO结构。其中一个字段是日期字段,但是API返回的日期格式与默认的time.Time解析格式不匹配,因此解组失败。
有没有办法在解组函数中指定要使用的日期格式来解析time.Time?我想使用正确定义的类型,使用字符串来保存日期时间字段感觉不对。
示例结构体:
type Transaction struct {
Id int64 `xml:"sequencenumber"`
ReferenceNumber string `xml:"ourref"`
Description string `xml:"description"`
Type string `xml:"type"`
CustomerID string `xml:"namecode"`
DateEntered time.Time `xml:"enterdate"` //这是问题所在的字段
Gross float64 `xml:"gross"`
Container TransactionDetailContainer `xml:"subfile"`
}
返回的日期格式是"yyyymmdd"。
英文:
I have XML data I am retrieving via a REST API that I am unmarshal-ing into a GO struct. One of the fields is a date field, however the date format returned by the API does not match the default time.Time parse format and thus the unmarshal fails.
Is there any way to specify to the unmarshal function which date format to use in the time.Time parsing? I'd like to use properly defined types and using a string to hold a datetime field feels wrong.
Sample struct:
type Transaction struct {
Id int64 `xml:"sequencenumber"`
ReferenceNumber string `xml:"ourref"`
Description string `xml:"description"`
Type string `xml:"type"`
CustomerID string `xml:"namecode"`
DateEntered time.Time `xml:"enterdate"` //this is the field in question
Gross float64 `xml:"gross"`
Container TransactionDetailContainer `xml:"subfile"`
}
The date format returned is "yyyymmdd".
答案1
得分: 61
我遇到了同样的问题。
time.Time
不满足 xml.Unmarshaler
接口。而且你不能指定日期格式。
如果你不想在之后处理解析,并且更喜欢让 xml.encoding
来处理,一个解决方案是创建一个带有匿名 time.Time
字段的结构体,并实现自己的 UnmarshalXML
方法来处理自定义的日期格式。
type Transaction struct {
//...
DateEntered customTime `xml:"enterdate"` // 使用满足 UnmarshalXML 接口的自定义类型
//...
}
type customTime struct {
time.Time
}
func (c *customTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
const shortForm = "20060102" // yyyymmdd 日期格式
var v string
d.DecodeElement(&v, &start)
parse, err := time.Parse(shortForm, v)
if err != nil {
return err
}
*c = customTime{parse}
return nil
}
如果你的 XML 元素使用属性作为日期,你需要以相同的方式实现 UnmarshalXMLAttr
。
参考:http://play.golang.org/p/EFXZNsjE4a
英文:
I had the same problem.
time.Time
doesn't satisfy the xml.Unmarshaler
interface. And you can not specify a date fomat.
If you don't want to handle the parsing afterward and you prefer to let the xml.encoding
do it, one solution is to create a struct with an anonymous time.Time
field and implement your own UnmarshalXML
with your custom date format.
<!-- language: go -->
type Transaction struct {
//...
DateEntered customTime `xml:"enterdate"` // use your own type that satisfies UnmarshalXML
//...
}
type customTime struct {
time.Time
}
func (c *customTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
const shortForm = "20060102" // yyyymmdd date format
var v string
d.DecodeElement(&v, &start)
parse, err := time.Parse(shortForm, v)
if err != nil {
return err
}
*c = customTime{parse}
return nil
}
If your XML element uses an attribut as a date, you have to implement UnmarshalXMLAttr the same way.
答案2
得分: 4
从我所了解的情况来看,encoding/xml存在一些已知的问题,这些问题被推迟到以后解决...
为了解决这个问题,可以使用string
类型而不是time.Time
类型,并在之后处理解析。
我在使用time.Parse解析以下格式的日期时遇到了很多麻烦:"Fri, 09 Aug 2013 19:39:39 GMT"
奇怪的是,我发现"net/http"有一个ParseTime函数,它接受一个字符串并且完美地工作...
http://golang.org/pkg/net/http/#ParseTime
英文:
From what I have read the encoding/xml has some known issues that have been put off until a later date...
To get around this issue, instead of using the type time.Time
use string
and handle the parsing afterwards.
I had quite a bit of trouble getting time.Parse to work with dates in the following format: "Fri, 09 Aug 2013 19:39:39 GMT"
Oddly enough I found that "net/http" has a ParseTime function that takes a string that worked perfectly...
http://golang.org/pkg/net/http/#ParseTime
答案3
得分: 1
我已经实现了一个符合规范的XML dateTime格式,你可以在GitHub上找到它:https://github.com/datainq/xml-date-time
你可以在W3C的规范中找到XML dateTime。
英文:
I've implemented a xml dateTime format conforming a spec, you can find it on GitHub: https://github.com/datainq/xml-date-time
You can find XML dateTime in W3C spec
答案4
得分: 0
一个细分技术,用于覆盖结构体中选择键的JSON编组/解组,同样适用于XML编组/解组,只需进行最小的修改。思路保持不变:将原始结构体别名以隐藏“Unmarshal”方法,并将该别名嵌入到另一个结构体中,其中可以覆盖各个字段以允许应用默认的解组方法。
在你的示例中,按照上述思路,我会这样做:
type TransactionA Transaction
type TransactionS struct {
*TransactionA
DateEntered string `xml:"enterdate"`
}
func (t *Transaction) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
const shortForm = "20060102" // yyyymmdd日期格式
var s = &TransactionS{TransactionA: (*TransactionA)(t)}
d.DecodeElement(s, &start)
t.DateEntered, _ = time.Parse(shortForm, s.DateEntered)
return nil
}
这种方法的最聪明之处在于,通过将TransactionS
发送到DecodeElement
,所有字段都通过嵌入的别名类型*TransactionA
填充到t
中,除了在S类型中被覆盖的字段。然后,您可以按照您的意愿处理被覆盖的结构体成员。
如果您有多个不同类型的字段需要以自定义方式处理,这种方法非常适用,并且您不需要引入一个无用的customType
,需要反复转换。
英文:
A niche technique to override the JSON marshaling/unmarshaling of select keys in a struct is also applicable to XML marshaling/unmarshaling, with minimal modifications. The idea remains the same: Alias the original struct to hide the Unmarshal
method, and embed the alias in another struct where individual fields may be overridden to allow the default unmarshaling method to apply.
In your example, following the idea above, I'd do it like this:
type TransactionA Transaction
type TransactionS struct {
*TransactionA
DateEntered string `xml:"enterdate"`
}
func (t *Transaction) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
const shortForm = "20060102" // yyyymmdd date format
var s = &TransactionS{TransactionA: (*TransactionA)(t)}
d.DecodeElement(s, &start)
t.DateEntered, _ = time.Parse(shortForm, s.DateEntered)
return nil
}
The smartest point of this method is, by sending TransactionS
into DecodeElement
, all fields of t
gets populated through the embedded alias type *TransactionA
, except for those overridden in the S-type. You can then process overridden struct members as you wish.
This approach scales very well if you have multiple fields of different types that you want to handle in a custom way, plus the benefit that you don't introduce an otherwise useless customType
that you have to convert over and over again.
答案5
得分: -2
const shortForm = "20060102" // yyyymmdd日期格式
它看起来难以理解。但在Go语言中是正确的。你可以在http://golang.org/src/time/format.go中阅读源代码。
英文:
const shortForm = "20060102" // yyyymmdd date format
It is unreadable. But it is right in Go. You can read the source in http://golang.org/src/time/format.go
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论