% @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 the Overview. -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 <<"null">>. -spec null(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_null. null(<>) 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 <<"true">> % or <<"false">>. -spec boolean(JSON :: binary()) -> {ReadValue :: binary(), Rest :: binary()} | not_boolean. boolean(<>) 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(<>) when ?IS_WHITESPACE(S) -> string(Rest); string(<<$", Rest/binary>>) -> string_inner(Rest, <<$">>); string(_) -> not_string. string_inner(<<$", Rest/binary>>, Acc) -> {<>, Rest}; string_inner(<<$\\, Escaped, Rest/binary>>, Acc) -> string_inner(Rest, <>); string_inner(<>, Acc) -> string_inner(Rest, <>); 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(<>) when ?IS_WHITESPACE(S) -> number(Rest); number(<<$-, Bin/binary>>) -> number_inner(Bin, <<$->>, false, false); number(<>) when C >= $0 andalso C =< $9 -> number_inner(Bin, <>, false, false); number(_) -> not_number. number_inner(<>, Acc, D, E) when H >= $0 andalso H =< $9 -> number_inner(T, <>, D, E); number_inner(<<$., T/binary>>, Acc, false, E) -> number_inner(T, <>, true, E); number_inner(<>, Acc, D, false) when (E =:= $e orelse E =:= $E) andalso (EM =:= $+ orelse EM =:= $-) -> number_inner(T, <>, D, true); number_inner(<>, Acc, D, false) when E =:= $e orelse E =:= $E -> number_inner(T, <>, 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 <<"[">> % 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(<>) 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 <<",">> or an empty binary will be returned. % % If the array is ending, the binary <<"]">> 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(<>) when ?IS_WHITESPACE(S) -> element(Rest); element(<>) when C =:= $] orelse C =:= $, -> {<>, Rest}; element(Bin) -> {<<>>, Bin}. % @doc Try to read an object from the JSON. % If there is an object, the binary <<"{">> % 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(<>) 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 <<"}">> % 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(<>) 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.