README.md

# XmerlC14n

[![Hex Version][hex-img]][hex] [![Hex Downloads][downloads-img]][downloads] [![License][license-img]][license]

[hex-img]: https://img.shields.io/hexpm/v/xmerl_c14n.svg
[hex]: https://hex.pm/packages/xmerl_c14n
[downloads-img]: https://img.shields.io/hexpm/dt/xmerl_c14n.svg
[downloads]: https://hex.pm/packages/xmerl_c14n
[license-img]: https://img.shields.io/badge/license-BSD-blue.svg
[license]: https://opensource.org/licenses/BSD-2-Clause

`XmerlC14n` canonicalizes XML according to the [Exclusive XML Canonicalization
specification version 1.0](http://www.w3.org/2001/10/xml-exc-c14n#), for use in
XML signatures.

It is a port to Elixir from the `xmerl_c14n` Erlang module found in the [esaml
project](https://github.com/arekinath/esaml).

Documentation is located at
[https://hexdocs.pm/xmerl_c14n](https://hexdocs.pm/xmerl_c14n)

## Installation

`XmerlC14n` can be installed by adding `xmerl_c14n` to your list of
dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:xmerl_c14n, "~> 0.1.0"}
  ]
end
```

## Examples

`XmerlC14n` operates on `xmerl` Erlang records, typically seen as tuples. These
can be parsed directly from the included Erlang modules in Elixir, using
`:xmerl_scan.string`, or with a nice wrapper, like
[SweetXml](https://github.com/kbrw/sweet_xml).

```elixir
iex> xml = ~S{<!DOCTYPE doc [<!ATTLIST e9 attr CDATA "default">]>
<doc>
   <e1   />
   <e2   ></e2>
   <e3   name = "elem3"   id="elem3"   />
   <e4   name="elem4"   id="elem4"   ></e4>
   <e5 a:attr="out" b:attr="sorted" attr2="all" attr="I'm"
      xmlns:b="http://www.ietf.org"
      xmlns:a="http://www.w3.org"
      xmlns="http://example.org"/>
   <e6 xmlns="" xmlns:a="http://www.w3.org">
      <e7 xmlns="http://www.ietf.org">
         <e8 xmlns="" xmlns:a="http://www.w3.org">
            <e9 xmlns="" xmlns:a="http://www.ietf.org"/>
         </e8>
      </e7>
   </e6>
</doc>
}
iex> {xml_tuples, _} = xml |> to_charlist |> :xmerl_scan.string(namespace_conformant: true, document: true)
{:xmlDocument,
  [
  {:xmlElement, :doc, :doc, [], {:xmlNamespace, [], []}, [], 1, [],
    [
    {:xmlText, [doc: 1], 1, [], '\n   ', :text},
    {:xmlElement, :e1, :e1, [], {:xmlNamespace, [], []}, [doc: 1], 2, [], [],
      [], '', :undeclared},
    {:xmlText, [doc: 1], 3, [], '\n   ', :text},
    {:xmlElement, :e2, :e2, [], {:xmlNamespace, [], []}, [doc: 1], 4, [], [],
      [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 5, [], '\n   ', :text},
    {:xmlElement, :e3, :e3, [], {:xmlNamespace, [], []}, [doc: 1], 6,
      [
      {:xmlAttribute, :name, :name, [], {:xmlNamespace, [], []},
        [e3: 6, doc: 1], 1, [], 'elem3', false},
      {:xmlAttribute, :id, :id, [], {:xmlNamespace, [], []}, [e3: 6, doc: 1],
        2, [], 'elem3', false}
      ], [], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 7, [], '\n   ', :text},
    {:xmlElement, :e4, :e4, [], {:xmlNamespace, [], []}, [doc: 1], 8,
      [
      {:xmlAttribute, :name, :name, [], {:xmlNamespace, [], []},
        [e4: 8, doc: 1], 1, [], 'elem4', false},
      {:xmlAttribute, :id, :id, [], {:xmlNamespace, [], []}, [e4: 8, doc: 1],
        2, [], 'elem4', false}
      ], [], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 9, [], '\n   ', :text},
    {:xmlElement, :e5, {:"http://example.org", :e5}, [],
      {:xmlNamespace, :"http://example.org",
        [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]}, [doc: 1],
      10,
      [
      {:xmlAttribute, :"a:attr", {:"http://www.w3.org", :attr},
        {'a', 'attr'},
        {:xmlNamespace, :"http://example.org",
          [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 1, [], 'out', false},
      {:xmlAttribute, :"b:attr", {:"http://www.ietf.org", :attr},
        {'b', 'attr'},
        {:xmlNamespace, :"http://example.org",
          [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 2, [], 'sorted', false},
      {:xmlAttribute, :attr2, :attr2, [],
        {:xmlNamespace, :"http://example.org",
          [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 3, [], 'all', false},
      {:xmlAttribute, :attr, :attr, [],
        {:xmlNamespace, :"http://example.org",
          [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 4, [], 'I\'m', false},
      {:xmlAttribute, :"xmlns:b", {'xmlns', 'b'}, {'xmlns', 'b'},
        {:xmlNamespace, :"http://example.org",
          [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 5, [], 'http://www.ietf.org', false},
        {:xmlAttribute, :"xmlns:a", {'xmlns', 'a'}, {'xmlns', 'a'},
          {:xmlNamespace, :"http://example.org",
            [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]},
          [e5: 10, doc: 1], 6, [], 'http://www.w3.org', false},
          {:xmlAttribute, :xmlns, :xmlns, [],
            {:xmlNamespace, :"http://example.org",
              [{'b', :"http://www.ietf.org"}, {'a', :"http://www.w3.org"}]},
            [e5: 10, doc: 1], 7, [], 'http://example.org', false}
      ], [], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 11, [], '\n   ', :text},
    {:xmlElement, :e6, {:"", :e6}, [],
      {:xmlNamespace, :"", [{'a', :"http://www.w3.org"}]}, [doc: 1], 12,
      [
      {:xmlAttribute, :xmlns, :xmlns, [],
        {:xmlNamespace, :"", [{'a', :"http://www.w3.org"}]}, [e6: 12, doc: 1],
        1, [], [], false},
      {:xmlAttribute, :"xmlns:a", {'xmlns', 'a'}, {'xmlns', 'a'},
        {:xmlNamespace, :"", [{'a', :"http://www.w3.org"}]}, [e6: 12, doc: 1],
        2, [], 'http://www.w3.org', false}
      ],
      [
      {:xmlText, [e6: 12, doc: 1], 1, [], '\n      ', :text},
      {:xmlElement, :e7, {:"http://www.ietf.org", :e7}, [],
        {:xmlNamespace, :"http://www.ietf.org", [{'a', :"http://www.w3.org"}]},
        [e6: 12, doc: 1], 2,
        [
        {:xmlAttribute, :xmlns, :xmlns, [], {:xmlNamespace, ...}, [...],
          ...}
        ],
        [
        {:xmlText, [e7: 2, e6: 12, doc: 1], 1, [], '\n         ', ...},
        {:xmlElement, :e8, {:"", ...}, [], ...},
        {:xmlText, [e7: 2, ...], 3, ...}
        ], [], :undefined, :undeclared},
      {:xmlText, [e6: 12, doc: 1], 3, [], '\n   ', :text}
      ], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 13, [], '\n', :text}
    ], [], '', :undeclared}
  ]}
iex> xml_tuples |> XmerlC14n.canonicalize!() |> IO.puts()
<doc>
  <e1></e1>
  <e2></e2>
  <e3 id="elem3" name="elem3"></e3>
  <e4 id="elem4" name="elem4"></e4>
  <e5 xmlns="http://example.org" xmlns:a="http://www.w3.org" xmlns:b="http://www.ietf.org" attr="I'm" attr2="all" b:attr="sorted" a:attr="out"></e5>
  <e6>
    <e7 xmlns="http://www.ietf.org">
      <e8 xmlns="">
        <e9></e9>
      </e8>
    </e7>
  </e6>
</doc>
```

If using [SweetXml](https://github.com/kbrw/sweet_xml), you neither have to
convert your XML to a charlist, nor destructure it from a tuple.

```elixir
iex> xml |> SweetXml.parse(namespace_conformant: true, document: true) |> XmerlC14n.canonicalize!() |> IO.puts()
<doc>
  <e1></e1>
  <e2></e2>
  <e3 id="elem3" name="elem3"></e3>
  <e4 id="elem4" name="elem4"></e4>
  <e5 xmlns="http://example.org" xmlns:a="http://www.w3.org" xmlns:b="http://www.ietf.org" attr="I'm" attr2="all" b:attr="sorted" a:attr="out"></e5>
  <e6>
    <e7 xmlns="http://www.ietf.org">
      <e8 xmlns="">
        <e9></e9>
      </e8>
    </e7>
  </e6>
</doc>
```

For more examples see https://hexdocs.pm/xmerl_c14n/XmerlC14n.html#canonicalize/1-examples