README.md

# 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.