An Elixir library for encoding and decoding Amazon Ion data.
Eyeon supports both Ion text (.ion) and Ion binary (.10n) formats.
It auto-detects binary input on decode, and you choose the output format
on encode via the :encoding option.
Quick start
# Decode Ion text
{:ok, value} = Eyeon.decode("{name: \"Alice\", scores: [98, 87, 95]}")
value["name"] #=> "Alice"
# Encode to Ion text (default)
{:ok, ion} = Eyeon.encode(%{"greeting" => "hello"})
# Encode to Ion binary
{:ok, bin} = Eyeon.encode(42, %{encoding: :binary})Reading and writing .ion files
# Write Ion text to a file
data = %{"name" => "Alice", "age" => 30, "active" => true}
File.write!("user.ion", Eyeon.encode!(data))
# Read it back
Eyeon.decode!(File.read!("user.ion"))
#=> %{"active" => true, "age" => 30, "name" => "Alice"}Reading and writing .10n (binary Ion) files
# Write Ion binary to a file
data = [1, 2, 3]
File.write!("data.10n", Eyeon.encode!(data, %{encoding: :binary}))
# Read it back — binary format is auto-detected
Eyeon.decode!(File.read!("data.10n"))
#=> [1, 2, 3]Ion types
Most Ion types map directly to Elixir types. Types without a native Elixir equivalent use tagged tuples:
| Ion type | Elixir representation |
|---|---|
null | nil |
bool | true / false |
int | integer |
float | float, :nan, :infinity, :neg_infinity |
decimal | Decimal.t() |
timestamp | DateTime.t(), NaiveDateTime.t(), or Date.t() |
string | binary string |
symbol | {:symbol, name} |
blob | {:blob, binary} |
clob | {:clob, binary} |
list | list |
struct | map with string keys |
sexp | {:sexp, list} |
annotation | {:annotated, {:symbol, name}, value} |
| typed null | {:null, type} e.g. {:null, :int} |
Timestamps
By default (:timestamp option set to :native), Ion timestamps are decoded
to native Elixir date/time types:
| Ion precision | Offset known? | Elixir type |
|---|---|---|
| Full (date + time) | Yes (Z, +HH:MM, -HH:MM) | DateTime.t() |
| Full (date + time) | Unknown (-00:00) | NaiveDateTime.t() |
Date only (YYYY-MM-DD) | n/a | Date.t() |
Year-month (YYYY-MMT) | n/a | Date.t() (day set to 1) |
Year only (YYYYT) | n/a | Date.t() (month and day set to 1) |
Fractional seconds beyond microseconds (6 digits) are truncated, since
Elixir's DateTime caps at microsecond precision.
Pass %{timestamp: :raw} to decode/2 to get {:timestamp, string} tuples
instead, which preserves the original Ion precision and offset semantics.
# Default: native types
Eyeon.decode!("2024-01-15T12:30:00Z")
#=> ~U[2024-01-15 12:30:00Z]
# Raw mode: tagged strings
Eyeon.decode!("2024-01-15T12:30:00Z", %{timestamp: :raw})
#=> {:timestamp, "2024-01-15T12:30:00Z"}The encoder accepts DateTime, NaiveDateTime, Date, and {:timestamp, string}
values in both text and binary modes.
Summary
Functions
Decodes Ion text or binary data to an Elixir value.
Decodes Ion text or binary data to an Elixir value, raising on error.
Encodes an Elixir value to Ion format.
Encodes an Elixir value to Ion format, raising on error.
Functions
Decodes Ion text or binary data to an Elixir value.
Binary Ion (starting with the Ion Version Marker 0xE0 0x01 0x00 0xEA)
is detected automatically; all other input is treated as Ion text.
Returns {:ok, value} on success or {:error, reason} on failure.
Options
:timestamp—:native(default) or:raw. When:native, timestamps are converted toDateTime,NaiveDateTime, orDate. When:raw, timestamps are returned as{:timestamp, string}tuples preserving the original Ion precision.:shared_symbol_tables— list ofEyeon.SharedSymbolTablestructs for resolving shared symbol table imports
Examples
iex> Eyeon.decode("42")
{:ok, 42}
iex> Eyeon.decode("true")
{:ok, true}
iex> Eyeon.decode("[1, 2, 3]")
{:ok, [1, 2, 3]}
iex> Eyeon.decode(~s({name: "Alice", age: 30}))
{:ok, %{"age" => 30, "name" => "Alice"}}
iex> Eyeon.decode("2023-01-15T10:30:00Z")
{:ok, ~U[2023-01-15 10:30:00Z]}
iex> Eyeon.decode("2023-01-15T10:30:00Z", %{timestamp: :raw})
{:ok, {:timestamp, "2023-01-15T10:30:00Z"}}
iex> Eyeon.decode("null.int")
{:ok, {:null, :int}}
iex> match?({:error, _}, Eyeon.decode("invalid ion !!!"))
true
Decodes Ion text or binary data to an Elixir value, raising on error.
Accepts the same options as decode/2.
Examples
iex> Eyeon.decode!("42")
42
iex> Eyeon.decode!("[1, 2, 3]")
[1, 2, 3]
iex> Eyeon.decode!(~s({name: "Alice", age: 30}))
%{"age" => 30, "name" => "Alice"}
iex> Eyeon.decode!("hello")
{:symbol, "hello"}
iex> Eyeon.decode!(<<0xE0, 0x01, 0x00, 0xEA, 0x21, 0x01>>)
1
iex> Eyeon.decode!("2024-01-15T10:30:00Z")
~U[2024-01-15 10:30:00Z]
iex> Eyeon.decode!("2024-01-15")
~D[2024-01-15]
@spec encode(any(), map()) :: {:ok, binary()} | {:error, Exception.t()}
Encodes an Elixir value to Ion format.
Returns {:ok, iodata} on success or {:error, reason} on failure.
Options
:encoding—:text(default) or:binary
Examples
iex> Eyeon.encode(42)
{:ok, "42"}
iex> Eyeon.encode(true)
{:ok, "true"}
iex> Eyeon.encode([1, 2, 3])
{:ok, "[1,2,3]"}
iex> {:ok, bin} = Eyeon.encode(42, %{encoding: :binary})
iex> is_binary(bin)
true
Encodes an Elixir value to Ion format, raising on error.
Examples
iex> Eyeon.encode!("hello")
~s("hello")
iex> Eyeon.encode!(nil)
"null"
iex> Eyeon.encode!(%{"key" => "value"})
~s({key:"value"})
iex> Eyeon.encode!({:symbol, "foo"})
"foo"
iex> Eyeon.encode!({:blob, "data"})
"{{ZGF0YQ==}}"