defmodule Jiffy do
|
|
@moduledoc """
|
|
A JSON parser as a NIF.
|
|
|
|
# Data Format
|
|
|
|
| Elixir | -> JSON | -> Elixir |
|
|
| ---------------------------- | ---------------- | ------- |
|
|
| `nil` | `null` | `nil` |
|
|
| `true` | `true` | `true` |
|
|
| `false` | `false` | `false` |
|
|
| `'hi'` | `[104, 105]` | `[104, 105]` |
|
|
| `"hi"` | `"hi"` | `"hi"` |
|
|
| `:hi` | `"hi"` | `"hi"` |
|
|
| `1` | `1` | `1` |
|
|
| `1.25` | `1.25` | `1.25` |
|
|
| `[]` | `[]` | `[]` |
|
|
| `[true, 1.0]` | `[true, 1.0]` | `[true, 1.0]` |
|
|
| `%{"foo" => "bar"}` | `{"foo": "bar"}` | `%{"foo" => "bar"}` |
|
|
| `%{foo: "bar"}` | `{"foo": "bar"}` | `%{"foo" => "bar"}` |
|
|
"""
|
|
@encode_options [:use_nil]
|
|
@decode_options [:use_nil, :return_maps]
|
|
|
|
@doc """
|
|
Encode a value to JSON.
|
|
|
|
# Unsupported structures
|
|
|
|
* Encoding Keywords currently is not supported.
|
|
* Encoding DateTime, Date or other Date-related Elixir structures will return
|
|
`{:error, {:invalid_ejson, any}}`. If you want to encode them - you need to cast
|
|
them to string before encoding.
|
|
|
|
# Options
|
|
|
|
* `:uescape` - Escapes UTF-8 sequences to produce a 7-bit clean output.
|
|
* `:pretty` - Produce JSON using two-space indentation.
|
|
* `:force_utf8` - Force strings to encode as UTF-8 by fixing broken
|
|
surrogate pairs and/or using the replacement character to remove
|
|
broken UTF-8 sequences in data.
|
|
* `:escape_forward_slashes` - Escapes the `/` character which can be
|
|
useful when encoding URLs in some cases.
|
|
* `{:bytes_per_red, n}` - Refer to the `decode/2` options.
|
|
* `{:bytes_per_iter, n}` - Refer to the `decode/2` options.
|
|
|
|
# Examples
|
|
|
|
iex> Jiffy.encode([1, 2, 3])
|
|
{:ok, "[1,2,3]"}
|
|
"""
|
|
@spec encode(any, opts :: :jiffy.encode_option()) :: {:ok, any()} | {:error, any()}
|
|
def encode(data, opts \\ []) do
|
|
{:ok, encode!(data, opts)}
|
|
catch
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
|
|
@doc """
|
|
Encode a value to JSON, raises an exception on error.
|
|
|
|
For list of options see `encode/2`.
|
|
|
|
# Examples
|
|
|
|
iex> Jiffy.encode!([1, 2, 3])
|
|
"[1,2,3]"
|
|
"""
|
|
@spec encode!(any, opts :: :jiffy.encode_option()) :: {:ok, any()} | no_return()
|
|
def encode!(data, opts \\ []) do
|
|
data
|
|
|> :jiffy.encode(@encode_options ++ opts)
|
|
|> :erlang.iolist_to_binary()
|
|
end
|
|
|
|
@doc """
|
|
Decode JSON to a value.
|
|
|
|
# Options
|
|
|
|
* `:return_trailer` - If any non-whitespace is found after the first
|
|
JSON term is decoded the return value of decode/2 becomes
|
|
`{:has_trailer, first_term, rest_iodata}`. This is useful to
|
|
decode multiple terms in a single binary.
|
|
* `{:bytes_per_red, n}` where `n` >= 0 - This controls the number of
|
|
bytes that Jiffy will process as an equivalent to a reduction. Each
|
|
20 reductions we consume 1% of our allocated time slice for the current
|
|
process. When the Erlang VM indicates we need to return from the NIF.
|
|
* `{:bytes_per_iter, n}` where `n` >= 0 - Backwards compatible option
|
|
that is converted into the `bytes_per_red` value.
|
|
|
|
# Examples
|
|
|
|
iex> Jiffy.decode("[1,2,3]")
|
|
{:ok, [1, 2, 3]}
|
|
"""
|
|
@spec decode(String.t(), opts :: :jiffy.decode_option()) :: {:ok, any()} | {:error, atom()}
|
|
def decode(data, opts \\ []) do
|
|
{:ok, decode!(data, opts)}
|
|
catch
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
|
|
@doc """
|
|
Decode JSON to a value, raises an exception on error.
|
|
|
|
For list of options see `decode/2`.
|
|
|
|
# Examples
|
|
|
|
iex> Jiffy.decode!("[1,2,3]")
|
|
[1, 2, 3]
|
|
"""
|
|
@spec decode!(String.t(), opts :: :jiffy.decode_option()) :: any() | no_return()
|
|
def decode!(data, opts \\ []) do
|
|
:jiffy.decode(data, @decode_options ++ opts)
|
|
end
|
|
end
|