XmerlC14n

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

XmerlC14n canonicalizes XML according to the Exclusive XML Canonicalization specification version 1.0, for use in XML signatures.

It is a port to Elixir from the xmerl_c14n Erlang module found in the esaml project.

Documentation is located at https://hexdocs.pm/xmerl_c14n

Installation

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

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.

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&#39;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, [], &#39;\n   &#39;, :text},
    {:xmlElement, :e1, :e1, [], {:xmlNamespace, [], []}, [doc: 1], 2, [], [],
      [], &#39;&#39;, :undeclared},
    {:xmlText, [doc: 1], 3, [], &#39;\n   &#39;, :text},
    {:xmlElement, :e2, :e2, [], {:xmlNamespace, [], []}, [doc: 1], 4, [], [],
      [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 5, [], &#39;\n   &#39;, :text},
    {:xmlElement, :e3, :e3, [], {:xmlNamespace, [], []}, [doc: 1], 6,
      [
      {:xmlAttribute, :name, :name, [], {:xmlNamespace, [], []},
        [e3: 6, doc: 1], 1, [], &#39;elem3&#39;, false},
      {:xmlAttribute, :id, :id, [], {:xmlNamespace, [], []}, [e3: 6, doc: 1],
        2, [], &#39;elem3&#39;, false}
      ], [], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 7, [], &#39;\n   &#39;, :text},
    {:xmlElement, :e4, :e4, [], {:xmlNamespace, [], []}, [doc: 1], 8,
      [
      {:xmlAttribute, :name, :name, [], {:xmlNamespace, [], []},
        [e4: 8, doc: 1], 1, [], &#39;elem4&#39;, false},
      {:xmlAttribute, :id, :id, [], {:xmlNamespace, [], []}, [e4: 8, doc: 1],
        2, [], &#39;elem4&#39;, false}
      ], [], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 9, [], &#39;\n   &#39;, :text},
    {:xmlElement, :e5, {:"http://example.org", :e5}, [],
      {:xmlNamespace, :"http://example.org",
        [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]}, [doc: 1],
      10,
      [
      {:xmlAttribute, :"a:attr", {:"http://www.w3.org", :attr},
        {&#39;a&#39;, &#39;attr&#39;},
        {:xmlNamespace, :"http://example.org",
          [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 1, [], &#39;out&#39;, false},
      {:xmlAttribute, :"b:attr", {:"http://www.ietf.org", :attr},
        {&#39;b&#39;, &#39;attr&#39;},
        {:xmlNamespace, :"http://example.org",
          [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 2, [], &#39;sorted&#39;, false},
      {:xmlAttribute, :attr2, :attr2, [],
        {:xmlNamespace, :"http://example.org",
          [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 3, [], &#39;all&#39;, false},
      {:xmlAttribute, :attr, :attr, [],
        {:xmlNamespace, :"http://example.org",
          [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 4, [], &#39;I\&#39;m&#39;, false},
      {:xmlAttribute, :"xmlns:b", {&#39;xmlns&#39;, &#39;b&#39;}, {&#39;xmlns&#39;, &#39;b&#39;},
        {:xmlNamespace, :"http://example.org",
          [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]},
        [e5: 10, doc: 1], 5, [], &#39;http://www.ietf.org&#39;, false},
        {:xmlAttribute, :"xmlns:a", {&#39;xmlns&#39;, &#39;a&#39;}, {&#39;xmlns&#39;, &#39;a&#39;},
          {:xmlNamespace, :"http://example.org",
            [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]},
          [e5: 10, doc: 1], 6, [], &#39;http://www.w3.org&#39;, false},
          {:xmlAttribute, :xmlns, :xmlns, [],
            {:xmlNamespace, :"http://example.org",
              [{&#39;b&#39;, :"http://www.ietf.org"}, {&#39;a&#39;, :"http://www.w3.org"}]},
            [e5: 10, doc: 1], 7, [], &#39;http://example.org&#39;, false}
      ], [], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 11, [], &#39;\n   &#39;, :text},
    {:xmlElement, :e6, {:"", :e6}, [],
      {:xmlNamespace, :"", [{&#39;a&#39;, :"http://www.w3.org"}]}, [doc: 1], 12,
      [
      {:xmlAttribute, :xmlns, :xmlns, [],
        {:xmlNamespace, :"", [{&#39;a&#39;, :"http://www.w3.org"}]}, [e6: 12, doc: 1],
        1, [], [], false},
      {:xmlAttribute, :"xmlns:a", {&#39;xmlns&#39;, &#39;a&#39;}, {&#39;xmlns&#39;, &#39;a&#39;},
        {:xmlNamespace, :"", [{&#39;a&#39;, :"http://www.w3.org"}]}, [e6: 12, doc: 1],
        2, [], &#39;http://www.w3.org&#39;, false}
      ],
      [
      {:xmlText, [e6: 12, doc: 1], 1, [], &#39;\n      &#39;, :text},
      {:xmlElement, :e7, {:"http://www.ietf.org", :e7}, [],
        {:xmlNamespace, :"http://www.ietf.org", [{&#39;a&#39;, :"http://www.w3.org"}]},
        [e6: 12, doc: 1], 2,
        [
        {:xmlAttribute, :xmlns, :xmlns, [], {:xmlNamespace, ...}, [...],
          ...}
        ],
        [
        {:xmlText, [e7: 2, e6: 12, doc: 1], 1, [], &#39;\n         &#39;, ...},
        {:xmlElement, :e8, {:"", ...}, [], ...},
        {:xmlText, [e7: 2, ...], 3, ...}
        ], [], :undefined, :undeclared},
      {:xmlText, [e6: 12, doc: 1], 3, [], &#39;\n   &#39;, :text}
      ], [], :undefined, :undeclared},
    {:xmlText, [doc: 1], 13, [], &#39;\n&#39;, :text}
    ], [], &#39;&#39;, :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&#39;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, you neither have to convert your XML to a charlist, nor destructure it from a tuple.

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&#39;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