Golang XML解析和time.Time字段

huangapple go评论84阅读模式
英文:

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:&quot;enterdate&quot;` // 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 = &quot;20060102&quot; // yyyymmdd date format
	var v string
	d.DecodeElement(&amp;v, &amp;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.

See http://play.golang.org/p/EFXZNsjE4a

答案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:&quot;enterdate&quot;`
}

func (t *Transaction) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    const shortForm = &quot;20060102&quot; // yyyymmdd date format
    var s = &amp;TransactionS{TransactionA: (*TransactionA)(t)}
    d.DecodeElement(s, &amp;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 = &quot;20060102&quot; // 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

huangapple
  • 本文由 发表于 2013年6月25日 23:16:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/17301149.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定