Last active
December 9, 2015 14:57
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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