# `JSV.Normalizer`
[🔗](https://github.com/lud/jsv/blob/v0.18.3/lib/jsv/normalizer.ex#L1)

A Normalizer for JSON data structures.

# `json_decoded_form`

```elixir
@type json_decoded_form() ::
  %{optional(String.t()) =&gt; json_decoded_form()}
  | [json_decoded_form()]
  | String.t()
  | number()
  | true
  | false
  | nil
```

# `default_on_general_atom`

```elixir
@spec default_on_general_atom(atom(), term()) :: {String.t(), term()}
```

Default implementation for the `:on_general_atom` option of `normalize/2` and
`normalize/3`. Transforms atoms to strings.

# `normalize`

```elixir
@spec normalize(
  term(),
  keyword()
) :: json_decoded_form()
```

Returns the given term in a JSON-decoded form without general atoms or
structs.

See `normalize/3` for details and options.

### Examples

    iex> JSV.Normalizer.normalize(%{name: :joe})
    %{"name" => "joe"}

    iex> JSV.Normalizer.normalize(%{"name" => :joe})
    %{"name" => "joe"}

    iex> JSV.Normalizer.normalize(%{"name" => "joe"})
    %{"name" => "joe"}

    iex> JSV.Normalizer.normalize(%{true: false})
    %{"true" => false}

    iex> JSV.Normalizer.normalize(%{specials: [true, false, nil]})
    %{"specials" => [true, false, nil]}

This function is also used internally to normalize schemas.

    iex> JSV.Normalizer.normalize(%JSV.Schema{title: nil, properties: nil})
    %{}

    iex> JSV.Normalizer.normalize(%JSV.Schema{type: :integer})
    %{"type" => "integer"}

    iex> JSV.Normalizer.normalize(%JSV.Schema{title: :"My Schema"})
    %{"title" => "My Schema"}

Other structs must implement the `JSV.Normalizer.Normalize` protocol.

    iex> defimpl JSV.Normalizer.Normalize, for: Range do
    iex>   def normalize(range), do: Map.from_struct(range)
    iex> end
    iex> JSV.Normalizer.normalize(1..10)
    %{"first" => 1, "last" => 10, "step" => 1}

# `normalize`

```elixir
@spec normalize(term(), term(), keyword()) :: {json_decoded_form(), term()}
```

Returns the given term in a JSON-decoded form without general atoms or structs
with an accumulator.

### What is "JSON-decoded" form?

By that we mean that the returned data could have been returned by
`JSON.decode!/1`:

* Only maps, lists, strings, numbers and atoms.
* Structs must implement the `JSV.Normalizer.Normalize` protocol.
* `true`, `false` and `nil` will be kept as-is in all places except for map
  keys.
* `true`, `false` and `nil` as map keys will be converted to string.
* Other atoms as values will be passed to the `:on_general_atom` callback (see
  options).
* Map keys must only be atoms, strings or numbers and will be converted to
  strings.

### Options

* `:on_general_atom` - A callback accepting an atom found in the data and the
  accumulator. Must return a JSON-decoded value.

### Examples

    iex> on_general_atom = fn atom, acc ->
    ...>   {"found:#{atom}", [atom|acc]}
    ...> end
    iex> opts = [on_general_atom: on_general_atom]
    iex> acc_in = []
    iex> JSV.Normalizer.normalize(%{an_atom: SomeAtom, a_string: "hello"}, acc_in, opts)
    {%{"an_atom" => "found:Elixir.SomeAtom", "a_string" => "hello"}, [SomeAtom]}

---

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