Skip to content

Instantly share code, notes, and snippets.

@garyposter
Last active December 9, 2015 14:57
Show Gist options
  • Save garyposter/27c9e0256b86ed687d60 to your computer and use it in GitHub Desktop.
Save garyposter/27c9e0256b86ed687d60 to your computer and use it in GitHub Desktop.
Some code snippets that show how you can use the erlang erlsom XML library, and Records, in Elixir.
# I wrote a blog version of this (http://codesinger.blogspot.com/2015/12/elixir-erlang-records-and-erlsom-xml.html), too.
# This example gets the XML for current observed weather at an airport from the
# NOAA. It then gets the associated XSD, and uses erlsom to pre-process it.
# This part can be run as an exs script.
import SweetXml
client = [{"User-agent", "Example Elixir Project"}]
options = [follow_redirect: true]
airport_code = "RDU"
url = Weathernow.NOAA.code_url(
"http://w1.weather.gov/xml/current_obs/K#{airport_code}.xml")
%HTTPoison.Response{body: body} = HTTPoison.get!(url, client, options)
schema_url = body |> xpath(
~x"//current_observation/@xsi:noNamespaceSchemaLocation")
%HTTPoison.Response{body: schema_body} = HTTPoison.get!(
schema_url, client, options)
File.write!(Path.join(__DIR__, "current_observation.xsd"), schema_body)
# Now we have an XSD.
# If you already have an XSD, this is all you need to ask erlsom to generate
# erlang header files to match it. We'll show how to use them in a moment.
:erlsom.write_xsd_hrl_file(
Path.join(__DIR__, "current_observation.xsd"),
Path.join(__DIR__, "current_observation.hrl"))
# As mentioned, verything above could be in a script.
# This code needs to be run as part of a module. Assume that the xsd and hrl
# files that we've already generated are in a "data" directory local to this
# file.
# This is the kind of thing you would do with any Record that loaded its data
# from a hrl file. 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:`.
require Record
xml_path = Path.join([__DIR__, "data", "current_observation.hrl"])
Record.defrecord :current_observation,
Record.extract(:current_observation, from: xml_path)
Record.defrecord :imageType,
Record.extract(:imageType, from: xml_path)
# This next bit shows pattern matching for a record. It's in the docs
# (http://elixir-lang.org/docs/stable/elixir/Record.html#defrecord/3), but I
# missed it the first time around.
# The code itself is converting the Records into maps. I would expect that an
# actual good usage of erlsom in Elixir would use the SAX parser to create maps
# or structs directly. I was just playing around, so I transformed this. My
# [first
# approach](https://github.com/garyposter/davethomas-elixir-chapter13-weathernow/commit/c7daffca6aeef418e70cd693849c38ed6d00c490#diff-f01c5009d33bcf4f3b165f2e9921e0ecR24)
# just used SweetXml, and was a *lot* simpler and shorter to do.
def erlsom_transform(data = current_observation()), do:
Enum.into(current_observation(data), Map.new, &_transform_value/1)
def erlsom_transform(data = imageType()), do:
Enum.into(imageType(data), Map.new, &_transform_value/1)
def erlsom_transform(data = [first | _rest]) when is_integer(first), do:
List.to_string(data)
def erlsom_transform(:undefined), do: nil
def erlsom_transform(data), do: data
defp _transform_value({k, v}), do: {k, erlsom_transform(v)}
# This creates an erlsom data structure from the XSD, which we use below.
{:ok, xsdModel} = :erlsom.compile_xsd_file(
Path.join([__DIR__, "data", "current_observation.xsd"]))
@xsdModel xsdModel
# This assembles averything of the above to get a parsed result of the doc,
# using the records generated by the doc. See [the erlsom
# docs](https://github.com/willemdj/erlsom#data-binder-mode) for details on
# what the :erlsom.scan is doing (and more details on the rest of this).
def handle_body(body) do
{:ok, data, _rest} = :erlsom.scan(body, @xsdModel)
erlsom_transform(data)
end
# You can see this in an exercise over here:
# https://github.com/garyposter/davethomas-elixir-chapter13-weathernow/blob/master/lib/weathernow/NOAA.ex
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment