zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

jason.ex (7888B)


      1 defmodule Jason do
      2   @moduledoc """
      3   A blazing fast JSON parser and generator in pure Elixir.
      4   """
      5 
      6   alias Jason.{Encode, Decoder, DecodeError, EncodeError, Formatter}
      7 
      8   @type escape :: :json | :unicode_safe | :html_safe | :javascript_safe
      9   @type maps :: :naive | :strict
     10 
     11   @type encode_opt :: {:escape, escape} | {:maps, maps} | {:pretty, boolean | Formatter.opts()}
     12 
     13   @type keys :: :atoms | :atoms! | :strings | :copy | (String.t() -> term)
     14 
     15   @type strings :: :reference | :copy
     16 
     17   @type floats :: :native | :decimals
     18 
     19   @type objects :: :maps | :ordered_objects
     20 
     21   @type decode_opt :: {:keys, keys} | {:strings, strings} | {:floats, floats} | {:objects, objects}
     22 
     23   @doc """
     24   Parses a JSON value from `input` iodata.
     25 
     26   ## Options
     27 
     28     * `:keys` - controls how keys in objects are decoded. Possible values are:
     29 
     30       * `:strings` (default) - decodes keys as binary strings,
     31       * `:atoms` - keys are converted to atoms using `String.to_atom/1`,
     32       * `:atoms!` - keys are converted to atoms using `String.to_existing_atom/1`,
     33       * custom decoder - additionally a function accepting a string and returning a key
     34         is accepted.
     35 
     36     * `:strings` - controls how strings (including keys) are decoded. Possible values are:
     37 
     38       * `:reference` (default) - when possible tries to create a sub-binary into the original
     39       * `:copy` - always copies the strings. This option is especially useful when parts of the
     40         decoded data will be stored for a long time (in ets or some process) to avoid keeping
     41         the reference to the original data.
     42 
     43     * `:floats` - controls how floats are decoded. Possible values are:
     44 
     45       * `:native` (default) - Native conversion from binary to float using `:erlang.binary_to_float/1`,
     46       * `:decimals` - uses `Decimal.new/1` to parse the binary into a Decimal struct with arbitrary precision.
     47 
     48     * `:objects` - controls how objects are decoded. Possible values are:
     49 
     50       * `:maps` (default) - objects are decoded as maps
     51       * `:ordered_objects` - objects are decoded as `Jason.OrderedObject` structs
     52 
     53   ## Decoding keys to atoms
     54 
     55   The `:atoms` option uses the `String.to_atom/1` call that can create atoms at runtime.
     56   Since the atoms are not garbage collected, this can pose a DoS attack vector when used
     57   on user-controlled data.
     58 
     59   ## Examples
     60 
     61       iex> Jason.decode("{}")
     62       {:ok, %{}}
     63 
     64       iex> Jason.decode("invalid")
     65       {:error, %Jason.DecodeError{data: "invalid", position: 0, token: nil}}
     66   """
     67   @spec decode(iodata, [decode_opt]) :: {:ok, term} | {:error, DecodeError.t()}
     68   def decode(input, opts \\ []) do
     69     input = IO.iodata_to_binary(input)
     70     Decoder.parse(input, format_decode_opts(opts))
     71   end
     72 
     73   @doc """
     74   Parses a JSON value from `input` iodata.
     75 
     76   Similar to `decode/2` except it will unwrap the error tuple and raise
     77   in case of errors.
     78 
     79   ## Examples
     80 
     81       iex> Jason.decode!("{}")
     82       %{}
     83 
     84       iex> Jason.decode!("invalid")
     85       ** (Jason.DecodeError) unexpected byte at position 0: 0x69 ("i")
     86 
     87   """
     88   @spec decode!(iodata, [decode_opt]) :: term | no_return
     89   def decode!(input, opts \\ []) do
     90     case decode(input, opts) do
     91       {:ok, result} -> result
     92       {:error, error} -> raise error
     93     end
     94   end
     95 
     96   @doc """
     97   Generates JSON corresponding to `input`.
     98 
     99   The generation is controlled by the `Jason.Encoder` protocol,
    100   please refer to the module to read more on how to define the protocol
    101   for custom data types.
    102 
    103   ## Options
    104 
    105     * `:escape` - controls how strings are encoded. Possible values are:
    106 
    107       * `:json` (default) - the regular JSON escaping as defined by RFC 7159.
    108       * `:javascript_safe` - additionally escapes the LINE SEPARATOR (U+2028)
    109         and PARAGRAPH SEPARATOR (U+2029) characters to make the produced JSON
    110         valid JavaScript.
    111       * `:html_safe` - similar to `:javascript_safe`, but also escapes the `/`
    112         character to prevent XSS.
    113       * `:unicode_safe` - escapes all non-ascii characters.
    114 
    115     * `:maps` - controls how maps are encoded. Possible values are:
    116 
    117       * `:strict` - checks the encoded map for duplicate keys and raises
    118         if they appear. For example `%{:foo => 1, "foo" => 2}` would be
    119         rejected, since both keys would be encoded to the string `"foo"`.
    120       * `:naive` (default) - does not perform the check.
    121 
    122     * `:pretty` - controls pretty printing of the output. Possible values are:
    123 
    124       * `true` to pretty print with default configuration
    125       * a keyword of options as specified by `Jason.Formatter.pretty_print/2`.
    126 
    127   ## Examples
    128 
    129       iex> Jason.encode(%{a: 1})
    130       {:ok, ~S|{"a":1}|}
    131 
    132       iex> Jason.encode("\\xFF")
    133       {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}}
    134 
    135   """
    136   @spec encode(term, [encode_opt]) ::
    137           {:ok, String.t()} | {:error, EncodeError.t() | Exception.t()}
    138   def encode(input, opts \\ []) do
    139     case do_encode(input, format_encode_opts(opts)) do
    140       {:ok, result} -> {:ok, IO.iodata_to_binary(result)}
    141       {:error, error} -> {:error, error}
    142     end
    143   end
    144 
    145   @doc """
    146   Generates JSON corresponding to `input`.
    147 
    148   Similar to `encode/1` except it will unwrap the error tuple and raise
    149   in case of errors.
    150 
    151   ## Examples
    152 
    153       iex> Jason.encode!(%{a: 1})
    154       ~S|{"a":1}|
    155 
    156       iex> Jason.encode!("\\xFF")
    157       ** (Jason.EncodeError) invalid byte 0xFF in <<255>>
    158 
    159   """
    160   @spec encode!(term, [encode_opt]) :: String.t() | no_return
    161   def encode!(input, opts \\ []) do
    162     case do_encode(input, format_encode_opts(opts)) do
    163       {:ok, result} -> IO.iodata_to_binary(result)
    164       {:error, error} -> raise error
    165     end
    166   end
    167 
    168   @doc """
    169   Generates JSON corresponding to `input` and returns iodata.
    170 
    171   This function should be preferred to `encode/2`, if the generated
    172   JSON will be handed over to one of the IO functions or sent
    173   over the socket. The Erlang runtime is able to leverage vectorised
    174   writes and avoid allocating a continuous buffer for the whole
    175   resulting string, lowering memory use and increasing performance.
    176 
    177   ## Examples
    178 
    179       iex> {:ok, iodata} = Jason.encode_to_iodata(%{a: 1})
    180       iex> IO.iodata_to_binary(iodata)
    181       ~S|{"a":1}|
    182 
    183       iex> Jason.encode_to_iodata("\\xFF")
    184       {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}}
    185 
    186   """
    187   @spec encode_to_iodata(term, [encode_opt]) ::
    188           {:ok, iodata} | {:error, EncodeError.t() | Exception.t()}
    189   def encode_to_iodata(input, opts \\ []) do
    190     do_encode(input, format_encode_opts(opts))
    191   end
    192 
    193   @doc """
    194   Generates JSON corresponding to `input` and returns iodata.
    195 
    196   Similar to `encode_to_iodata/1` except it will unwrap the error tuple
    197   and raise in case of errors.
    198 
    199   ## Examples
    200 
    201       iex> iodata = Jason.encode_to_iodata!(%{a: 1})
    202       iex> IO.iodata_to_binary(iodata)
    203       ~S|{"a":1}|
    204 
    205       iex> Jason.encode_to_iodata!("\\xFF")
    206       ** (Jason.EncodeError) invalid byte 0xFF in <<255>>
    207 
    208   """
    209   @spec encode_to_iodata!(term, [encode_opt]) :: iodata | no_return
    210   def encode_to_iodata!(input, opts \\ []) do
    211     case do_encode(input, format_encode_opts(opts)) do
    212       {:ok, result} -> result
    213       {:error, error} -> raise error
    214     end
    215   end
    216 
    217   defp do_encode(input, %{pretty: true} = opts) do
    218     case Encode.encode(input, opts) do
    219       {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded)}
    220       other -> other
    221     end
    222   end
    223 
    224   defp do_encode(input, %{pretty: pretty} = opts) when pretty !== false do
    225     case Encode.encode(input, opts) do
    226       {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded, pretty)}
    227       other -> other
    228     end
    229   end
    230 
    231   defp do_encode(input, opts) do
    232     Encode.encode(input, opts)
    233   end
    234 
    235   defp format_encode_opts(opts) do
    236     Enum.into(opts, %{escape: :json, maps: :naive})
    237   end
    238 
    239   defp format_decode_opts(opts) do
    240     Enum.into(opts, %{keys: :strings, strings: :reference, floats: :native, objects: :maps})
    241   end
    242 end