Sunday, May 20, 2012

Go and XML

Background

After months of lurking in the Go forums and following #golang and Go's reddit feed,  I decided to dive in with my first "real" Go project creating a API client library for Recurly.  I'm still in the middle of finishing the project but already I find Go easy to use and I find myself being very productive. One particular area of productivity, is with processing XML.  Recurly only returns xml  at the time of this writing, so marshalling/unmarshalling XML to structs was going to be very important for this project.



Where to start

The package documentation on golang.org is excellent and great place to start learning Go.  Go and it's authors seem to have libraries for all your needs, yet the language still doesn't seem bloated.  The particular package I was going to deal with is "endcoding/xml".

Show me the code

Go makes dealing with XML extremely simple.  Since Recurly sends back structured XML objects, I needed to create structs for those objects.  Here is an example.



The good thing here is that I could name my fields whatever I liked because I can override the field mappings.  This way, if I don't like underscores, I can use camel case and still have the fields map out properly.  By adding the XMLName attribute, the parent tag for the XML can be overridden.


Now if I want to unmarshal the XML, I just need to load in the response body and pass the struct  to have the XML data loaded.



I can also very easily update the struct then marshal the data back into XML where it can be placed in the body of a put request.



You can even nest structs within structs or slices of structs within structs.


There are also many more options like being able to reference attributes, ignore fields, and even inserting a field as a comment.  More information on these options here.

Issues

Issues that I found weren't really with Go itself but more with using Go with Recurly since Recurly doesn't allow extra fields in their XML.  For instance, if I was to get an account, a field that is returned is "created_at" which references the time created.  Now if I go to update the account,  I will get an error back saying "Invalid XML.  <created_at> not a valid field."  The way I worked around it was to copy the struct and empty out fields that were not part of the list of accepted fields.  That and setting the xml option of "omitempty" would cause the field to not be created during the marshalling process.  I was thinking of using regexp to filter out unwanted fields in the raw XML but didn't implement.  If anyone has any other suggestions, leave a comment.


Conclusion


As you can see, dealing with XML in Go can be quick and easy, making you a more productive coder. I will be posting more on Go so stay tuned.





6 comments:

  1. Just a small nitpick, it's not "GO", it's Go. See http://golang.org/ for example.

    ReplyDelete
  2. Treat errors right away and leave them behind, no need to nest so deeply:

    body, err := ioutil.ReadAll(resp.Body)
    if readerr != nil {
    return readerr
    }

    err = xml.Unmarshal(body, &account)
    if err != nil {
    return err
    }

    return nil

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. For anyone else that comes along: unexported ie: lower-case fields in a struct will not be Marshalled. So you do not need to empty out struct fields, just don't export them.

    ReplyDelete
  5. You can also give a struct field a tag `xml:"-"` and it will skip it during marshal/unmarshal.

    ReplyDelete