Tuesday, December 8, 2015

Elixir: Erlang records and the Erlsom XML library

I wanted to jot down some notes about Elixir, because I'm learning it, and because some of the pieces I assembled for my most recent code exercise were hard to find across the web. Hopefully it will help someone else, or at least be something I can refer to again in the future.

I was playing around implementing the last exercise from Chapter 13 of Dave Thomas' Programming Elixir book: get the XML for the current observed weather at an airport from the NOAA's observations (example), and render it. First I used SweetXML, which is a wrapper around the standard Erlang XML library xmerl. This was pleasant and easy (once I figured out the XPath syntax for getting a tag name).

(Yes, I could have used the Erlang xmerl library directly, but it was easier for me with SweetXML.)

However, when digging around for Elixir and Erlang XML tools, I encountered the Erlang SAX-based XML library erlsom, and in particular it's "Data Binder" mode. On the face of it, it seemed like what I wanted: parse a doc into a simple data structure that I could then use elsewhere. Maybe it would be simpler than the SweetXML approach, where I had to rebuild a map from the XPath results!

This is not the case, at least in Elixir. :-) However, digging down into that rabbit hole revealed some interesting bits that were hard-ish to find, and maybe worth sharing for others. If you are interested in exploring the erlsom library from Elixir, or if you are interfacing with Erlang from Elixir and need to use Records, this might be mildly valuable for you.

To start off with the pretty bit, eventually my function to parse the XML and give me the data structure I had chosen ended up looking nice enough.

To get there took a bit of doing, though. First, you need to get the associated xsd file. Then you need to convert it to an Erlang .hrl (header or C-style include) file using an erlsom function. You can see my probably-not-particularly-fantastic way of doing that over here, if you are interested.

Then, when I needed to use that .hrl file so I could use it to interpret the scanned data from erlsom, I had to include it in Elixir somehow. I had to piece together that I was supposed to use a Record, and I found some examples on how to feed a .hrl file into it.

Other examples you'll find around use `from_lib:` instead of `from:`. I think `from_lib:` is for using data from compiled libraries, and `from:` is for files local to your project? I'm not clear, but I needed `from:`.

We'll use those in the `erlsom_transform` function you saw in the first gist. You might have also noticed the `@xsdModel` in that code. It came from this.

Now the only piece left is the `erlsom_transform` function. I wanted a map, not a Record. I needed to pattern match against a Record, which I overlooked in the docs–lines 1 and 3 below are simple examples, but you can also pattern-match on fields in the Record.

For reference, this all resulted in a data structure that looked like this. I never bothered to figure out something pleasant to do with the `anyAttribs` bits.

If I were actually interested in working with XML I might explore how to use the erlsom SAX parser to directly produce Elixir structs instead. I am just exploring though, and the world around me uses JSON, not XML.

I hope that helps someone else! If you want to see this live in my poorly tested, poorly commented exercise, have at it.

1 comment:

Unknown said...

Very helpful. Thank you for sharing.