Anže's Blog

Python, Django, and the Web

30 Apr 2015

The Tale of Two Structs

I’ve been using golang for the past few days and I really like it. It’s fast, it’s typesafe, it’s easy to learn and it has concurrency built right into the language.

I have, however, stumbled into a bit of a pickle. file and store the parsed data into a database. It feels like this could simply be accomplished in a single struct with multiple annotations - some for sql some for xml, but I can’t seem to get it to work.

The XML file is quite simple:

<?xml version="1.0" encoding="utf-8"?>
<weatherdata>
  <meta>
    <lastupdate>2015-04-18T09:18:16</lastupdate>
    <nextupdate>2015-04-19T22:00:00</nextupdate>
  </meta>
  <sun rise="2015-04-18T06:15:37" set="2015-04-18T19:52:34" />
</weatherdata>

And this is my struct that is used by GORM:

type Update struct {
	gorm.Model
	LastUpdate time.Time
	NextUpdate time.Time
	Rise       time.Time
	Set        time.Time
}

First problem

Parsing the dates fails, because the date strings in XML are missing the timezone:

Parsing time "2015-04-30T09:05:19" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "Z07:00"

I actually managed to solve that by writing my own UnmarshalXML and UnmarshalXMLAttr functions (see here), but then I couldn’t get GORM to save my custom Time struct.

My current solution is to use string for date fields and then parse them manually (see below).

Second problem

I can’t seem to get the rise attr of the sun element without a sun struct in between:

type UpdateXml struct {
	LastUpdate string `xml:"meta>lastupdate"`
	NextUpdate string `xml:"meta>nextupdate"`
	Sun        struct {
		Rise string `xml:"rise,attr"`
		Set  string `xml:"set,attr"`
	} `xml:"sun"`
}

My solution

So my current solution is to use two structs and a convert function to convert between the two:

// Helper function for converting UpdateXml -> Update
func (u *UpdateXml) ToUpdate() Update {
	return Update{
		LastUpdate: parse(u.LastUpdate),
		NextUpdate: parse(u.NextUpdate),
		Rise:       parse(u.Sun.Rise),
		Set:        parse(u.Sun.Set),
	}
}