@@ -13,7 +13,7 @@ defprotocol JSON.Encoder do
1313
1414 @derive JSON.Encoder
1515 defstruct ...
16-
16+
1717 > #### Leaking Private Information {: .error}
1818 >
1919 > The `:except` approach should be used carefully to avoid
@@ -150,6 +150,13 @@ defimpl JSON.Encoder, for: Map do
150150 end
151151end
152152
153+ defmodule JSON.DecodeError do
154+ @ moduledoc """
155+ The exception raised by `JSON.decode!/1`.
156+ """
157+ defexception [ :message , :offset , :data ]
158+ end
159+
153160defmodule JSON do
154161 @ moduledoc ~S"""
155162 JSON encoding and decoding.
@@ -192,7 +199,7 @@ defmodule JSON do
192199
193200 @ moduledoc since: "1.18.0"
194201
195- @ type decode_error ::
202+ @ type decode_error_reason ::
196203 { :unexpected_end , non_neg_integer ( ) }
197204 | { :invalid_byte , non_neg_integer ( ) , byte ( ) }
198205 | { :unexpected_sequence , non_neg_integer ( ) , binary ( ) }
@@ -211,11 +218,11 @@ defmodule JSON do
211218
212219 The error tuple will have one of the following reasons.
213220
214- * `{:unexpected_end, position }` if `binary` contains incomplete JSON value
215- * `{:invalid_byte, position , byte}` if `binary` contains unexpected byte or invalid UTF-8 byte
216- * `{:unexpected_sequence, position , bytes}` if `binary` contains invalid UTF-8 escape
221+ * `{:unexpected_end, offset }` if `binary` contains incomplete JSON value
222+ * `{:invalid_byte, offset , byte}` if `binary` contains unexpected byte or invalid UTF-8 byte
223+ * `{:unexpected_sequence, offset , bytes}` if `binary` contains invalid UTF-8 escape
217224 """
218- @ spec decode ( binary ( ) ) :: { :ok , term ( ) } | { :error , decode_error ( ) }
225+ @ spec decode ( binary ( ) ) :: { :ok , term ( ) } | { :error , decode_error_reason ( ) }
219226 def decode ( binary ) when is_binary ( binary ) do
220227 with { decoded , :ok , rest } <- decode ( binary , :ok , [ ] ) do
221228 if rest == "" do
@@ -250,7 +257,7 @@ defmodule JSON do
250257
251258 For streaming decoding, see Erlang's `:json` module.
252259 """
253- @ spec decode ( binary ( ) , term ( ) , keyword ( ) ) :: { term ( ) , term ( ) , binary ( ) } | decode_error ( )
260+ @ spec decode ( binary ( ) , term ( ) , keyword ( ) ) :: { term ( ) , term ( ) , binary ( ) } | decode_error_reason ( )
254261 def decode ( binary , acc , decoders ) when is_binary ( binary ) and is_list ( decoders ) do
255262 decoders = Keyword . put_new ( decoders , :null , nil )
256263
@@ -261,14 +268,14 @@ defmodule JSON do
261268 { :error , { :unexpected_end , byte_size ( binary ) } }
262269
263270 :error , { :invalid_byte , byte } ->
264- { :error , { :invalid_byte , position ( __STACKTRACE__ ) , byte } }
271+ { :error , { :invalid_byte , offset ( __STACKTRACE__ ) , byte } }
265272
266273 :error , { :unexpected_sequence , bytes } ->
267- { :error , { :unexpected_sequence , position ( __STACKTRACE__ ) , bytes } }
274+ { :error , { :unexpected_sequence , offset ( __STACKTRACE__ ) , bytes } }
268275 end
269276 end
270277
271- defp position ( stacktrace ) do
278+ defp offset ( stacktrace ) do
272279 with [ { _ , _ , _ , opts } | _ ] <- stacktrace ,
273280 % { cause: % { position: position } } <- opts [ :error_info ] do
274281 position
@@ -293,14 +300,23 @@ defmodule JSON do
293300 { :ok , decoded } ->
294301 decoded
295302
296- { :error , { :unexpected_end , position } } ->
297- raise ArgumentError , "unexpected end of JSON binary at position #{ position } "
298-
299- { :error , { :invalid_byte , position , byte } } ->
300- raise ArgumentError , "invalid byte #{ byte } at position #{ position } "
301-
302- { :error , { :unexpected_sequence , position , bytes } } ->
303- raise ArgumentError , "unexpected sequence #{ inspect ( bytes ) } at position #{ position } "
303+ { :error , { :unexpected_end , offset } } ->
304+ raise JSON.DecodeError ,
305+ message: "unexpected end of JSON binary at position (byte offset) #{ offset } " ,
306+ data: binary ,
307+ offset: offset
308+
309+ { :error , { :invalid_byte , offset , byte } } ->
310+ raise JSON.DecodeError ,
311+ message: "invalid byte #{ byte } at position (byte offset) #{ offset } " ,
312+ data: binary ,
313+ offset: offset
314+
315+ { :error , { :unexpected_sequence , offset , bytes } } ->
316+ raise JSON.DecodeError ,
317+ message: "unexpected sequence #{ inspect ( bytes ) } at position (byte offset) #{ offset } " ,
318+ data: binary ,
319+ offset: offset
304320 end
305321 end
306322
@@ -316,6 +332,7 @@ defmodule JSON do
316332 "[123,\" string\" ,{\" key\" :\" value\" }]"
317333
318334 """
335+ @ spec encode! ( a , ( a -> iodata ( ) ) ) :: binary ( ) when a: var
319336 def encode! ( term , encoder \\ & encode_value / 2 ) do
320337 IO . iodata_to_binary ( encoder . ( term , encoder ) )
321338 end
@@ -336,6 +353,7 @@ defmodule JSON do
336353 "[123,\" string\" ,{\" key\" :\" value\" }]"
337354
338355 """
356+ @ spec encode! ( a , ( a -> iodata ( ) ) ) :: iodata ( ) when a: var
339357 def encode_to_iodata! ( term , encoder \\ & encode_value / 2 ) do
340358 encoder . ( term , encoder )
341359 end
0 commit comments