XML To JSON in Golang Demystified
There comes a point in every developer’s life where XML data is painfully unavoidable to work with. RSS Feeds try to make this easier but everyone prefers good ol’ JSON, unless you are a sadist.
In Golang we can attack this in a number of ways like using an XML to JSON Library, but why not use the Go standard libraries and save some vendoring issues.
Building our own XML to JSON function
I was recently working on something that required a Medium RSS feed published to a website and thought why not use a GoLang function running on OpenFaaS to accomplish this.
Create a new function
To create our new function we need to use the OpenFaaS CLI
$ faas new medium2json --lang=go
Now we have a super simple Golang handler for OpenFaaS.
Import our Libraries
There are a few libraries required by our function. So let’s import them. Open *function/handler.go *and add the imports under package
|
|
You will notice I added net/http so let’s make use of it.
GETing our RSS Feed
Regular Medium users will know that an RSS feed is exposed for each user at https://medium.com/feed/@affix
We can utilize this feed to get what we need using a simple HTTP GET with the net/http package in Go. Let’s add it to our Handle function:
|
|
The above code is pretty simple, but I always get asked about the defer statement.
What exactly can happen if you don’t defer?
The answer is pretty simple. Without defering the Close(), you create a resource leak. While the application is running, the resource used to create the request can never be closed and the file descriptor will not be released.
Do I need to Close the Request?
If the Body is not both read to EOF and closed, the Client’s underlying RoundTripper (typically Transport) may not be able to re-use a persistent TCP connection to the server for a subsequent “keep-alive” request.
Parse that XML
Now that we have some data, we should parse it. But before we do that we need a structure to unmarshal the data into.
In the handler.go file above the function lets create a struct to match the data we require. The beauty of the GoLang unmarshal function allows us to specify in the struct only the fields we require.
|
|
If you look at the XML returned by the Medium Feed, you will notice I have omitted a lot of unnecessary fields like Channel.Title and Channel.Image so the xml.Unmarshal function will not attempt to add those fields to the struct.
You may also notice I have a Posts []struct that doesn’t appear on the RSS Feed. This comes from the items element of the XML Document specified at the end of the struct with xml:”item”* *the unmarshal function will take all item elements and parse them into the Posts []struct.
So let’s parse that data into the struct in the Handle function:
|
|
The Unmarshal function parses the XML-encoded data and stores the result in the value pointed to by v, which must be an arbitrary struct, slice, or string. Well-formed data that does not fit into v is discarded.
Because Unmarshal uses the reflect package, it can only assign to exported fields. Unmarshal uses a case-sensitive comparison to match XML element names to tag values and struct field names.
Convert to JSON and Return
Now we have our structure we can Marshal it to JSON.
|
|
Marshal returns the JSON encoding of the input interface.
Marshal traverses the value v recursively. If an encountered value implements the Marshaler interface and is not a nil pointer, Marshal calls its MarshalJSON method to produce JSON.
Since the Marshal function produces a []byte we need to convert this to a string when returning from our Handle function using string().
Deploy to OpenFaaS
Now we can deploy our function to our OpenFaaS cluster:
$ faas up -f medium2json.yml
$ faas up -f medium2json.yml [0] > Building medium2json. Clearing temporary build folder: ./build/medium2json/ Preparing ./medium2json/ ./build/medium2json//function Building: affixxx/medium2json:latest with go template. Please wait.. Sending build context to Docker daemon 7.68kB
Once you have your function deployed you can test it using curl:
curl -X POST -d “affix” [https://gateway.faas.kfj.io/function/medium2json](https://gateway.faas.kfj.io/function/medium2json)
curl -X POST -d “affix” https://gateway.faas.kfj.io/function/medium2json [{“Title”:”#FaasFriday — Building a Serverless Microblog…
The above URL is currently deployed, feel free to see the results using it.
TLDR; The Full Function Code
|
|