defmodule JSX do def encode!(term, opts \\ []) do parser_opts = :jsx_config.extract_config(opts ++ [:escaped_strings]) parser(:jsx_to_json, opts, parser_opts).(JSX.Encoder.json(term) ++ [:end_json]) end def encode(term, opts \\ []) do { :ok, encode!(term, opts) } rescue ArgumentError -> { :error, :badarg } end def decode!(json, opts \\ []) do decoder_opts = :jsx_config.extract_config(opts) case decoder(JSX.Decoder, opts ++ [:return_maps], decoder_opts).(json) do { :incomplete, _ } -> raise ArgumentError result -> result end end def decode(term, opts \\ []) do { :ok, decode!(term, opts) } rescue ArgumentError -> { :error, :badarg } end def format!(json, opts \\ []) do case :jsx.format(json, opts) do { :incomplete, _ } -> raise ArgumentError result -> result end end def format(json, opts \\ []) do { :ok, format!(json, opts) } rescue ArgumentError -> { :error, :badarg } end def minify!(json), do: format!(json, [space: 0, indent: 0]) def minify(json) do { :ok, minify!(json) } rescue ArgumentError -> { :error, :badarg } end def prettify!(json), do: format!(json, [space: 1, indent: 2]) def prettify(json) do { :ok, prettify!(json) } rescue ArgumentError -> { :error, :badarg } end def is_json?(json, opts \\ []) do case :jsx.is_json(json, opts) do { :incomplete, _ } -> false result -> result end rescue _ -> false end def is_term?(term, opts \\ []) do parser_opts = :jsx_config.extract_config(opts) parser(:jsx_verify, opts, parser_opts).(JSX.Encoder.json(term) ++ [:end_json]) rescue _ -> false end def encoder(handler, initialstate, opts) do :jsx.encoder(handler, initialstate, opts) end def decoder(handler, initialstate, opts) do :jsx.decoder(handler, initialstate, opts) end def parser(handler, initialstate, opts) do :jsx.parser(handler, initialstate, opts) end end defmodule JSX.Decoder do def init(opts) do :jsx_to_term.init(opts) end def handle_event({ :literal, :null }, state), do: :jsx_to_term.insert(:nil, state) def handle_event(event, state), do: :jsx_to_term.handle_event(event, state) end defprotocol JSX.Encoder do @fallback_to_any true def json(term) end defimpl JSX.Encoder, for: Map do def json(map) do [:start_object] ++ unpack(map, Map.keys(map)) end defp unpack(map, [k|rest]) when is_integer(k) or is_binary(k) or is_atom(k) do [k] ++ JSX.Encoder.json(Map.get(map, k)) ++ unpack(map, rest) end defp unpack(_, []), do: [:end_object] end defimpl JSX.Encoder, for: List do def json([]), do: [:start_array, :end_array] def json([{}]), do: [:start_object, :end_object] def json([{ _, _ }|_] = list) do [:start_object] ++ unzip(list) end def json(list) do [:start_array] ++ unhitch(list) end defp unzip([{k, v}|rest]) when is_integer(k) or is_binary(k) or is_atom(k) do [k] ++ JSX.Encoder.json(v) ++ unzip(rest) end defp unzip([]), do: [:end_object] defp unhitch([v|rest]) do JSX.Encoder.json(v) ++ unhitch(rest) end defp unhitch([]), do: [:end_array] end defimpl JSX.Encoder, for: Atom do def json(nil), do: [:null] def json(true), do: [true] def json(false), do: [false] def json(atom), do: [atom] end defimpl JSX.Encoder, for: [Integer, Float, BitString] do def json(value), do: [value] end defimpl JSX.Encoder, for: HashDict do def json(dict), do: JSX.Encoder.json(HashDict.to_list(dict)) end defimpl JSX.Encoder, for: [Range, Stream, HashSet] do def json(enumerable) do JSX.Encoder.json(Enum.to_list(enumerable)) end end defimpl JSX.Encoder, for: [Tuple, PID, Port, Reference, Function, Any] do def json(map) when is_map(map), do: JSX.Encoder.Map.json(Map.delete(map, :__struct__)) def json(_), do: raise ArgumentError end