# FML
**Flexible**, **fast**, **fun** XML parser in Elixir, powered by erlsom.
It has nothing to do with my life or your life.
## Installation
```elixir def deps do
[
{:fml, "~> 0.1.0"}
]
end
```
## Why FML?
FML is powered by erlsom SAX parser, so it has all the perks that a SAX parser
provides:
* Less overhead, no intermediate DOM tree representation created.
* The result after parsing is done is the desired end result.
* Somewhat xpath support, you can trace back to the parents of the element using
the parsing stack.
## Usage
`FML` is a wrapper around erlsom SAX parser, so to get started you need to define
a list of events happen during parsing time.
Callbacks to implement.
* `on_start_element(element, stack, state)`
* `on_end_element(element, stack, state)`
**Element** is the current element that being parsed (begin tag or end tag), is a
3-element tuple of tag name, attributes and tag content
**Stack** is the current elements stack during parsing time, including all the
parents of the current element. For example, if we
are handling `bar` element ending event in the following XML.
```
<?xml version="1.0"?>
<rss version="2.0">
<foo>
<bar>The awesome RSS</bar>
</foo>
</rss>
```
Then the stack will be a list of two element: `<foo>` and `<rss>`, note that
element would look like the element specified above.
State is the current state of your result, basically the data you would like to
retrieve after done parsing.
## Example
Given an RSS file as below
```xml
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>The awesome RSS</title>
<dc:creator><![CDATA[Cẩm Huỳnh]]></dc:creator>
</channel>
</rss>
```
Implement all the required callbacks and handle elements that we are interested
in.
```elixir
defmodule MyRSSParser do
@behaviour FML.Parser
def on_start_element({'channel', _, _}, _stack, state) do
Map.put(state, :channel, %{})
end
# Match other element start events and ignore
def on_start_element(_element, _stack, state) do
state
end
# Handle channel/title element
def on_end_element({'title', attributes, content}, [{'channel', _, _} | _], state) do
%{channel: channel} = state
channel = Map.put(channel, :title, content)
Map.put(state, :channel, channel)
end
# Handle channel/dc:creator element
def on_end_element({{'dc', 'creator'}, _, content}, [{'channel', _, _} | _], state) do
%{channel: channel} = state
channel = Map.put(channel, :creator, content)
Map.put(state, :channel, channel)
end
def on_end_element(_element, _stack, state) do
state
end
end
```
Now start parsing.
```elixir
FML.parse(the_rss_string_above, _init_state = %{}, MyRSSParser)
%{
title: "The awesome RSS",
creator: "Cẩm Huỳnh"
}
```
## Contribute
1. Fork the repo.
2. Write the code.
3. Submit the pull request.