# `SignCore.XML.C14n.XmerlC14n`
[🔗](https://github.com/utaladriz/pkcs11ex/blob/v0.1.0/lib/sign_core/xml/c14n/xmerl_c14n.ex#L1)

Vendored copy of `xmerl_c14n` (DoggettCK/xmerl_c14n on Hex, derived
from esaml's Erlang implementation).

Functions for performing XML canonicalization (c14n), as specified
at [http://www.w3.org/TR/xml-c14n](http://www.w3.org/TR/xml-c14n).

## Why vendored

The Hex package (last release Sep 2019) crashes on OTP 28 when an
unprefixed attribute carries `[]` in its namespace field rather
than `{:xmlNamespace, [], []}`. We vendor + patch rather than
carrying a broken dependency.

## Local edits vs. upstream Hex 0.2.0 (BSD-2-Clause)

  * Module renamed `XmerlC14n` → `SignCore.XML.C14n.XmerlC14n`.
  * `do_canonical_name/3` gains a clause for non-`:xmlNamespace`
    namespace fields — returns the bare local name (exc-c14n's
    rule for unprefixed attributes living in no namespace).

Original copyright in `LICENSE.md` in this directory. The above
patches are © 2026 the pkcs11ex maintainers, BSD-2-Clause-compatible.

# `canonicalize`

```elixir
@spec canonicalize(entity :: xml_type() | String.t()) ::
  {:ok, String.t()} | {:error, {:failed_canonicalization, term()}}
```

Given an `xmerl` XML tuple (element/attribute/etc...) or string
representation of XML, returns the canonical binary version of the XML it
represents.

Returns either `{:ok, String.t()}` if canonicalized successfully, or
`{:error, {:failed_canonicalization, term}}` if canonicalization failed.

If the `preserve_comments` argument is true, preserves comments in the output. Any
namespace prefixes listed in `inclusive_namespaces` will be left as they are and not
modified during canonicalization.

```
# Given xml
<!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> {:ok, canonicalized_xml} = XmerlC14n.canonicalize(xml)
iex> IO.puts(canonicalized_xml)
<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>
```

# `canonicalize`

```elixir
@spec canonicalize(entity :: xml_type() | String.t(), preserve_comments :: boolean()) ::
  {:ok, String.t()} | {:error, {:failed_canonicalization, term()}}
```

# `canonicalize`

```elixir
@spec canonicalize(
  entity :: xml_type() | String.t(),
  preserve_comments :: boolean(),
  inclusive_namespaces :: []
) :: {:ok, String.t()} | {:error, {:failed_canonicalization, term()}}
```

# `canonicalize!`

```elixir
@spec canonicalize!(entity :: xml_type() | String.t()) :: String.t()
```

Given an `xmerl` XML tuple (element/attribute/etc...) or string
representation of XML, returns the canonical binary version of the XML it
represents.

Returns either `String.t()` if canonicalized successfully, or raises an
`ArgumentError` if canonicalization failed.

If the `preserve_comments` argument is true, preserves comments in the output. Any
namespace prefixes listed in `inclusive_namespaces` will be left as they are and not
modified during canonicalization.

```
# Given xml
<!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> XmerlC14n.canonicalize!(xml) |> 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>
```

# `canonicalize!`

```elixir
@spec canonicalize!(entity :: xml_type() | String.t(), preserve_comments :: boolean()) ::
  String.t()
```

# `canonicalize!`

```elixir
@spec canonicalize!(
  entity :: xml_type() | String.t(),
  preserve_comments :: boolean(),
  inclusive_namespaces :: []
) :: String.t()
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
