Current section
Files
Jump to
Current section
Files
src/jsonpull_read.erl
% @doc These are the most basic functions you can use to read data from JSON.
% They take the value you ask for out of binary JSON if it's there.
% They return {Bin, Rest} or an atom representing an error. They will also skip whitespace.
%
% For more information on how to use these functions and what a pull parser is,
% read <a href="overview-summary.html">the Overview</a>.
-module(jsonpull_read).
-export([
null/1,
boolean/1,
string/1,
number/1,
array/1,
element/1,
object/1,
key/1
]).
-define(IS_WHITESPACE(X), X =:= $\040 % Space
orelse X =:= $\011 % Horizontal tab
orelse X =:= $\012 % Line feed
orelse X =:= $\015). % Carriage return
% @doc Try to read a null from the JSON.
% If it's there, it will be returned as a binary <code><<"null">></code>.
-spec null(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_null.
null(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> null(Rest);
null(<<"null", Rest/binary>>) ->
{<<"null">>, Rest};
null(_) ->
not_null.
% @doc Try to read a boolean from the JSON.
% If it's there, it will be returned as a binary <code><<"true">></code>
% or <code><<"false">></code>.
-spec boolean(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_boolean.
boolean(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> boolean(Rest);
boolean(<<"true", Rest/binary>>) ->
{<<"true">>, Rest};
boolean(<<"false", Rest/binary>>) ->
{<<"false">>, Rest};
boolean(_) ->
not_boolean.
% @doc Try to read a string from the JSON.
% If it's there, it will be returned without any sort of processing.
% This means escape sequences and the quotation marks on the edges are still there.
%
% @see jsonpull:unescape_and_strip/1
-spec string(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_string | unterminated_string.
string(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> string(Rest);
string(<<$", Rest/binary>>) ->
string_inner(Rest, <<$">>);
string(_) ->
not_string.
string_inner(<<$", Rest/binary>>, Acc) ->
{<<Acc/binary, $">>, Rest};
string_inner(<<$\\, Escaped, Rest/binary>>, Acc) ->
string_inner(Rest, <<Acc/binary, $\\, Escaped>>);
string_inner(<<C, Rest/binary>>, Acc) ->
string_inner(Rest, <<Acc/binary, C>>);
string_inner(<<>>, _) ->
unterminated_string.
% @doc Try to read a number from the JSON.
% If it's there, it will be returned as a binary string in its original form.
-spec number(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_number.
number(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> number(Rest);
number(<<$-, Bin/binary>>) ->
number_inner(Bin, <<$->>, false, false);
number(<<C, Bin/binary>>) when C >= $0 andalso C =< $9 ->
number_inner(Bin, <<C>>, false, false);
number(_) ->
not_number.
number_inner(<<H, T/binary>>, Acc, D, E) when H >= $0 andalso H =< $9 ->
number_inner(T, <<Acc/binary, H>>, D, E);
number_inner(<<$., T/binary>>, Acc, false, E) ->
number_inner(T, <<Acc/binary, $.>>, true, E);
number_inner(<<E, EM, T/binary>>, Acc, D, false)
when (E =:= $e orelse E =:= $E) andalso (EM =:= $+ orelse EM =:= $-) ->
number_inner(T, <<Acc/binary, E, EM>>, D, true);
number_inner(<<E, T/binary>>, Acc, D, false) when E =:= $e orelse E =:= $E ->
number_inner(T, <<Acc/binary, E>>, D, true);
number_inner(Rest, Number, _, _) ->
{Number, Rest}.
% @doc Try to read an array from the JSON.
% If an array is starting here, the binary <code><<"[">></code>
% will be returned.
%
% Structured types are handled differently in pull parsers. For more info, read the Overview.
-spec array(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_array.
array(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> array(Rest);
array(<<$[, Rest/binary>>) ->
{<<$[>>, Rest};
array(_) ->
not_array.
% @doc Try to read the next array element from the JSON.
% If there is an element, either the binary <code><<",">></code> or an empty binary will be returned.
%
% If the array is ending, the binary <code><<"]">></code> will be returned.
%
% Structured types are handled differently in pull parsers. For more info, read the Overview.
-spec element(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()}.
element(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> element(Rest);
element(<<C, Rest/binary>>) when C =:= $] orelse C =:= $, ->
{<<C>>, Rest};
element(Bin) ->
{<<>>, Bin}.
% @doc Try to read an object from the JSON.
% If there is an object, the binary <code><<"{">></code>
% will be returned.
%
% Structured types are handled differently in pull parsers. For more info, read the Overview.
-spec object(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_object.
object(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> object(Rest);
object(<<${, Rest/binary>>) ->
{<<${>>, Rest};
object(_) ->
not_object.
% @doc Try to read the next object key from the JSON.
% If there is a key, it will be returned as a raw string,
% with the same caveats as {@link string/1}.
%
% If the object is ending, the binary <code><<"}">></code>
% will be returned.
%
% Structured types are handled differently in pull parsers. For more info, read the Overview.
-spec key(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | missing_colon_after_key | unterminated_key | key_not_string.
key(<<S, Rest/binary>>) when ?IS_WHITESPACE(S) -> key(Rest);
key(<<$}, Rest/binary>>) ->
{<<$}>>, Rest};
key(Bin) ->
KeyAndRest = case Bin of
<<$,, R/binary>> -> R;
R -> R
end,
case string(KeyAndRest) of
{Key, <<$:, Rest/binary>>} -> {Key, Rest};
{_, _} -> missing_colon_after_key;
unterminated_string -> unterminated_key;
not_string -> key_not_string
end.