%%============================================================================== %% Copyright 2021-2024 Jan Henry Nystrom %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %%============================================================================== %%%------------------------------------------------------------------- %%% @doc %%% A JSON stream library based on: %%% The JavaScript Object Notation (JSON) Data Interchange Format (rfc8259) %%% JavaScript Object Notation (JSON) Pointer (rfc6901) %%% JavaScript Object Notation (JSON) Patch (rfc6902) %%% JSON Merge Patch (rfc7396) %%% %%% JSON is represented as follows: %%% %%% value : true | false | null | object | array | number | string %%% pointer : top | [integer | string | '-'] %%% patch : [object] %%% merge : object %%% %%% object : map %%% array : [value] %%% string : UTF-8 binary %%% number : integer() | float() %%% true : atom(true) %%% false : atom(false) %%% null : atom(null) %%% %%% Strings can be represented by atoms when generating JSON, but will not %%% not be generated when converting JSON to erlang or any of the functions %%% returning JSON. %%% %%% When converting Erlang terms to JSON iolists are generated but %%% it can generate a binary if so instructed. %%% %%% Objects as maps, multiple occurrences of the members is not supported. %%% %%% @end %%% %% @author Jan Henry Nystrom %% @copyright (C) 2021-2024, Jan Henry Nystrom %%%------------------------------------------------------------------- -module(jhn_json). -copyright('Jan Henry Nystrom '). %% Library functions -export([encode/1, encode/2, decode/1, decode/2, next/1, next/2, eval/2, eval/3, patch/2, merge/2 ]). %% Exported types -export_type([json/0, pointer/0, patch/0, merge/0]). -export_type([jstring/0, object/0, array/0]). %% Types -type json() :: true | false | null | number() | jstring() | object() | array(). -type jstring() :: binary(). -type object() :: #{jstring() => json()}. -type array() :: [json()]. -type cont() :: {decode, stack()} | {object, {complete(), expect()}, acc(), stack()} | {array, {first(), complete()}, array(), stack()} | {string, binary(), stack()} | {unescape, binary(), stack()} | {number, stage(), phase(), list(), stack()}. -type eval_cont() :: {eval_binary, path(), stream(), path()} | {eval_dash, expect(), integer(), stream(), path()} | {eval_array, integer(), expect(), stream(),path(),path()} | {eval_object, binary(), expect(), stream(),path(),path()} | {eval_key, binary(), stream(), path(), path()} | {skip_value, skip_cont()} | {skip_base, string(), skip_cont()} | {skip_object, expect(), skip_cont()} | {skip_string, skip_cont()} | {skip_colon, boolean(), skip_cont()} | {skip_number, skip_cont()} | {unwind, path(), json()} | {unwind_array, unwind_cont()} | {unwind_object, unwind_cont()} | {unwind_name, unwind_cont()} | {unwind_colon, unwind_cont()} | {unwind_string, unwind_cont()} | {unwind_value, unwind_cont()} | {unwind_number, unwind_cont()} | {unwind_base, string(), unwind_cont()} | {decode, path(), cont()}. -type skip_cont() :: {eval_binary, path(), stream(), path()} | {eval_dash, expect(), integer(), stream(), path()} | {eval_array, integer(), expect(), stream(),path(),path()} | {eval_object, binary(), expect(), stream(),path(),path()} | {skip_object, expect(), skip_cont()} | {skip_array, expect(), skip_cont()} | {skip_value, skip_cont()}. -type unwind_cont() :: {unwind, path(), json()} | {unwind_array, unwind_cont()} | {unwind_object, unwind_cont()}. -type stream() :: boolean(). -type complete() :: boolean(). -type expect() :: name | comma | colon. -type first() :: boolean(). -type stage() :: sign | zero | pre | post. -type phase() :: int | float | exp. -type acc() :: [{jstring(), json()}]. -type stack() :: [{array, array()} | {name, acc()} | {value, {name(), acc()}}]. -type path() :: [integer() | binary()]. -type name() :: jstring(). %% FIXME -type merge() :: object(). -type patch() :: [object()]. -type opt() :: stream | decode | pointer | binary. -type next_cont() :: _. -type opts() :: [opt()]. -type pointer() :: top | ['-' | integer() | jstring() | atom()]. %% Defines %% Char macros -define(NULL, 0). -define(BEL, 7). -define(BS, 8). -define(HT, 9). -define(LF, 10). -define(VT, 11). -define(FF, 12). -define(CR, 13). -define(SPC, 32). %% Decode macros -define(IS_INT(C), C>=$0, C=<$9). -define(IS_POS_INT(C), C>=$1, C=<$9). -define(IS_SIGN(C), C == $-; C == $+). -define(IS_EXP(C), C==$E; C==$e). -define(ZERO_OR_POST(Stage), Stage == zero; Stage == post). -define(EXP_ZERO_OR_POST(C, Stage), ((Stage == zero) orelse (Stage == post)) andalso ((C == $E) orelse (C == $e))). %% Eval macros -define(WS(WS), WS == ?HT; WS == ?LF; WS == ?CR; WS == ?SPC). %% =================================================================== %% Library functions. %% =================================================================== %%-------------------------------------------------------------------- %% Function: encode(Term) -> JSON. %% @doc %% Encodes the structured Erlang term as an iolist. %% Equivalent of encode(Term, iolist) -> JSON. %% @end %%-------------------------------------------------------------------- -spec encode(json()) -> iodata(). %%-------------------------------------------------------------------- encode(true) -> <<"true">>; encode(false) -> <<"false">>; encode(null) -> <<"null">>; encode(Object = #{}) -> encode_object(Object); encode([]) -> <<"[]">>; encode(List = [_ | _]) -> encode_array(List); encode(I) when is_integer(I) -> integer_to_binary(I); encode(F) when is_float(F) -> erlang:float_to_binary(F, [short]); encode(String) when is_binary(String) -> encode_string(String); encode(String) when is_atom(String) -> encode_string(atom_to_binary(String, utf8)). %%-------------------------------------------------------------------- %% Function: encode(Term, [Option]) -> JSON. %% @doc %% Encodes the structured Erlang term as an iolist or binary. %% Encode will raise an exception if the erlang term is not well formed. %% Options are: %% pointer -> the term represents a pointer %% binary -> a binary is returned %% iolist -> an iolist is returned (default) %% @end %%-------------------------------------------------------------------- -spec encode(json() | pointer(), opts()) -> iolist() | binary(). %%-------------------------------------------------------------------- encode(T, [binary, _]) -> iolist_to_binary(encode_pointer(T, [])); encode(T, [_, binary]) -> iolist_to_binary(encode_pointer(T, [])); encode(T, [_, _]) -> encode_pointer(T, []); encode(T, [pointer]) -> encode_pointer(T, []); encode(T, [binary]) -> iolist_to_binary(encode(T)); encode(T, _) -> encode(T). %%-------------------------------------------------------------------- %% Function: decode(JSON) -> {Term, Binary} | {more, Continuation}. %% @doc %% Decodes the binary if a JSON Pointer into the Erlang respresentation %% and if a JSON value into a structured Erlang term and the remaining binary %% is ignored. %% @end %%-------------------------------------------------------------------- -spec decode(binary()) -> json() | pointer(). %%-------------------------------------------------------------------- decode(B) -> decode(B, []). %%-------------------------------------------------------------------- %% Function: decode(JSON, Continuation) -> {Term, Binary} | {more,Continuation}. %% @doc %% Supports the decoding of when supplied with the the option stream as the %% option with no options works as decode/1. %% Decoding a stream of JSON the functions returns either a tuple of a %% complete json value and the remainder of the stream (binary) or %% {more, Continuation} where subsequent calls to decode/2 is made with %% futher data and the Continuation as the second argument. %% Options are: %% stream -> enables the decoding of a stream of JSON values. %% @end %%-------------------------------------------------------------------- -spec decode(binary(), cont() | opts()) -> pointer() | json() | {json(), binary()} | {more, cont()}. %%-------------------------------------------------------------------- decode(B, {decode, S}) -> do_decode(B, true, S); decode(B, {base, Check, V, S}) -> base(Check, B, V, true, S); decode(B, {object, State, Acc, S}) -> object(B, State, Acc, true,S); decode(B, {array, State, Acc, S}) -> array(B, State, Acc, true, S); decode(B, {string, Acc, S}) -> string(B, Acc, true, S); decode(B, {unescape, Acc, S}) -> unescape(B, Acc, true, S); decode(B, {unescape_hex, Hex, Acc, S}) -> unescape_hex(B, Hex, Acc, true, S); decode(B, {number, State, Phase, Acc, S}) -> number(B, State, Phase, Acc, true, S); decode(<<>>, []) -> top; decode(B, []) -> case do_decode(B, false, []) of {more, _} -> erlang:error(badarg); {JSON, _} -> JSON; Pointer -> Pointer end; decode(B, [stream]) -> do_decode(B, true, []). %%-------------------------------------------------------------------- %% Function: next(Binary) -> {Binary, Binary} | {more, Continuation}. %% @doc %% Picks the first json on a stream and returns that and the rest or %% {more, Continuation} where Continuation is used in a call to %% decode(Binary, Continuation). %% @end %%-------------------------------------------------------------------- -spec next(binary()) -> {binary(), binary()} | {more, next_cont()}. %%-------------------------------------------------------------------- next(B) -> do_next(B). %%-------------------------------------------------------------------- %% Function: next(Binary, Cont) -> {Binary, Binary} | {more, Cont}. %% @doc %% Picks the first json on a stream given a continuation and returns that %% and the rest or {more Continuation} is if it was not a complete value. %% @end %%-------------------------------------------------------------------- -spec next(binary(), next_cont()) -> {binary(), binary()} | {more, next_cont()}. %%-------------------------------------------------------------------- next(T, {next_value, Acc, C}) -> next_value(T, Acc, C); next(T, {next_base, Base, Acc, C}) -> next_base(Base, T, Acc, C); next(T, {next_array, Expect, Acc, C}) -> next_array(T, Expect, Acc, C); next(T, {next_object, Expect, Acc, C}) -> next_object(T, Expect, Acc,C); next(T, {next_name, Acc, C}) -> next_name(T, Acc, C); next(T, {next_colon, Flag, Acc, C}) -> next_colon(T, Flag, Acc, C); next(T, {next_string, Acc, C}) -> next_string(T, Acc, C); next(T, {next_unescape, Acc, C}) -> next_unescape(T, Acc, C); next(T, {next_unescape_hex, Hex, Acc, C}) -> next_unescape_hex(T, Hex, Acc, C); next(T, {next_number, Acc, C}) -> next_number(T, Acc, C). %%-------------------------------------------------------------------- %% Function: eval(JSONPointer | Continuation, Binary | JSON) -> JSON. %% @doc %% Selects and decodes a Fragment of a JSON document based on the Pointer. %% Both the pointer and the JSON can be either a binary or the Erlang %% representation. If the JSON is a binary and not the complete JSON fragment %% eval/2 returns {more, Continuation} and eval is called again with %% Continuation and more of the stream as eval(Continuation, Binary). %% @end %%-------------------------------------------------------------------- -spec eval(binary() | pointer(), binary() | json()) -> json() | {more, cont()} | {error, _}; (eval_cont(), binary()) -> {json(), binary()} | {more, cont()} | {error, _}. %%-------------------------------------------------------------------- eval({eval_binary, P, S, Path}, T) -> eval_binary(P, T, S, Path); eval({eval_dash, Expect, Size, S, P}, T) -> eval_dash(T, Expect, Size, S, P); eval({eval_array, N, Expect,S,P,Rest},T) -> eval_array(N, T, Expect,S,P,Rest); eval({eval_object,Key,Expect,S,P,Rest},T) -> eval_object(Key,T,Expect,S,P,Rest); eval({eval_key, Key, S, Path, Rest}, T) -> eval_key(T, Key, S, Path, Rest); eval({skip_value, C}, T) -> skip_value(T, C); eval({skip_base, Base, C}, T) -> skip_base(Base, T, C); eval({skip_object, Expect, C}, T) -> skip_object(T, Expect, C); eval({skip_string, C}, T) -> skip_string(T, C); eval({skip_colon, Flag, C}, T) -> skip_colon(T, Flag, C); eval({skip_number, C}, T) -> skip_number(T, C); eval({unwind, P, JSON}, T) -> unwind(P, T, JSON); eval({unwind_array, C}, T) -> unwind_array(T, C); eval({unwind_object, C}, T) -> unwind_object(T, C); eval({unwind_name, C}, T) -> unwind_name(T, C); eval({unwind_colon, C}, T) -> unwind_colon(T, C); eval({unwind_string, C}, T) -> unwind_string(T, C); eval({unwind_value, C}, T) -> unwind_value(T, C); eval({unwind_number, C}, T) -> unwind_number(T, C); eval({unwind_base, Expect, C}, T) -> unwind_base(Expect, T, C); eval({decode, Path, Cont}, T) -> case decode(T, Cont) of {more, Cont} -> {more, {decode, Path, Cont}}; {JSON, T} -> unwind(Path, T, JSON) end; eval(Pointer, J) -> eval(Pointer, J, []). %%-------------------------------------------------------------------- -spec eval(binary() | pointer(), binary() | json(), opts()) -> json() | {json(), binary()} | {more, cont()} | {error, _}. %%-------------------------------------------------------------------- eval(top, B, []) when is_binary(B) -> decode(B); eval(top, J, []) -> J; eval(Pointer, JSON, Opts) when is_binary(Pointer) -> eval(decode(Pointer), JSON, Opts); eval(top, B, [stream]) when is_binary(B) -> eval_binary([], B, true, []); eval(Pointer, B, []) when is_binary(B) -> eval_binary(Pointer, B, false, []); eval(Pointer, B, [stream]) when is_binary(B) -> eval_binary(Pointer, B, true, []); eval(Pointer, J, []) -> eval_json(Pointer, J, []). %%-------------------------------------------------------------------- %% Function: patch(Patch, JSON) -> JSON. %% @doc %% Patch applies the JSON Patch to a JSON value represented as Erlang %% returning the patched JSON or error if one of the test operations in the %% JSON Patch failed. %% @end %%-------------------------------------------------------------------- -spec patch(patch(), json()) -> json() | error. %%-------------------------------------------------------------------- patch([], JSON) -> JSON; patch([Op = #{<<"op">> := <<"test">>} | Ops], JSON) -> case apply_op(Op, JSON) of true -> patch(Ops, JSON); false -> error end; patch([Op | Ops], JSON) -> patch(Ops, apply_op(Op, JSON)). %%-------------------------------------------------------------------- %% Function: merge(Patch, JSON) -> JSON. %% @doc %% Merge applies a JSON Merge Patch to the JSON represented as Erlang %% returning a new JSON object represented as Erlang. %% @end %%-------------------------------------------------------------------- -spec merge(merge(), json()) -> json(). %%-------------------------------------------------------------------- merge(Patch = #{}, JSON = #{}) -> maps:fold(fun merge_object/3, JSON, Patch); merge(Patch = #{}, _) -> merge(Patch, #{}); merge(Patch, _) -> Patch. %% =================================================================== %% Encoding %% =================================================================== encode_pointer(top, _) -> <<>>; encode_pointer([], []) -> <<$/>>; encode_pointer([], Acc) -> lists:reverse(Acc); encode_pointer([H | T], Acc) when is_binary(H) -> encode_pointer(T, [[$/, pointer_escape(H)] | Acc]); encode_pointer(['-' | T], Acc) -> encode_pointer(T, [[$/, $-] | Acc]); encode_pointer([H | T], Acc) when is_atom(H) -> encode_pointer([atom_to_binary(H, utf8) | T], Acc); encode_pointer([H | T], Acc) when is_integer(H), H >= 0 -> encode_pointer(T, [integer_to_binary(H), $/ | Acc]); encode_pointer(_, _) -> erlang:error(badarg). pointer_escape(String) -> case pointer_escapeable(String) of true -> pointer_escape(String, <<>>); false -> String end. pointer_escapeable(<<>>) -> false; pointer_escapeable(<<$~, _/binary>>) -> true; pointer_escapeable(<<$/, _/binary>>) -> true; pointer_escapeable(<<_, T/binary>>) -> pointer_escapeable(T). pointer_escape(<<>>, Acc) -> Acc; pointer_escape(<<$~, T/binary>>, Acc) -> pointer_escape(T, <>); pointer_escape(<<$/, T/binary>>, Acc) -> pointer_escape(T, <>); pointer_escape(<>, Acc) -> pointer_escape(T, <>). encode_object(Object) -> case maps:fold(fun element/3, [], Object) of [] -> <<"{}">>; [_ | Members] -> [<<"{">>, Members, <<"}">>] end. element(N, V, Acc) -> [<<",">>, encode(N), <<":">>, encode(V) | Acc]. encode_array(A) -> [_ | Es] = lists:foldr(fun(E, Acc) -> [<<",">>, encode(E) |Acc] end, [], A), [<<"[">>, Es, <<"]">>]. encode_string(String) -> case escapeable(String) of true -> [<<"\"">>, escape(String, <<>>), <<"\"">>]; false -> [<<"\"">>, String, <<"\"">>] end. escapeable(<<>>) -> false; escapeable(<<0, _/binary>>) -> true; escapeable(<<1, _/binary>>) -> true; escapeable(<<2, _/binary>>) -> true; escapeable(<<3, _/binary>>) -> true; escapeable(<<4, _/binary>>) -> true; escapeable(<<5, _/binary>>) -> true; escapeable(<<6, _/binary>>) -> true; escapeable(<<7, _/binary>>) -> true; escapeable(<<8, _/binary>>) -> true; escapeable(<<9, _/binary>>) -> true; escapeable(<<10, _/binary>>) -> true; escapeable(<<11, _/binary>>) -> true; escapeable(<<12, _/binary>>) -> true; escapeable(<<13, _/binary>>) -> true; escapeable(<<14, _/binary>>) -> true; escapeable(<<15, _/binary>>) -> true; escapeable(<<16, _/binary>>) -> true; escapeable(<<17, _/binary>>) -> true; escapeable(<<18, _/binary>>) -> true; escapeable(<<19, _/binary>>) -> true; escapeable(<<20, _/binary>>) -> true; escapeable(<<21, _/binary>>) -> true; escapeable(<<22, _/binary>>) -> true; escapeable(<<23, _/binary>>) -> true; escapeable(<<24, _/binary>>) -> true; escapeable(<<25, _/binary>>) -> true; escapeable(<<26, _/binary>>) -> true; escapeable(<<27, _/binary>>) -> true; escapeable(<<28, _/binary>>) -> true; escapeable(<<29, _/binary>>) -> true; escapeable(<<30, _/binary>>) -> true; escapeable(<<31, _/binary>>) -> true; escapeable(<<34, _/binary>>) -> true; escapeable(<<47, _/binary>>) -> true; escapeable(<<92, _/binary>>) -> true; escapeable(<<_/utf8, T/binary>>) -> escapeable(T). escape(<<>>, Acc) -> Acc; escape(<>, Acc) -> escape(T, <>); escape(<<1, T/binary>>, Acc) -> escape(T, <>); escape(<<2, T/binary>>, Acc) -> escape(T, <>); escape(<<3, T/binary>>, Acc) -> escape(T, <>); escape(<<4, T/binary>>, Acc) -> escape(T, <>); escape(<<5, T/binary>>, Acc) -> escape(T, <>); escape(<<6, T/binary>>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>); escape(<<14, T/binary>>, Acc) -> escape(T, <>); escape(<<15, T/binary>>, Acc) -> escape(T, <>); escape(<<16, T/binary>>, Acc) -> escape(T, <>); escape(<<17, T/binary>>, Acc) -> escape(T, <>); escape(<<18, T/binary>>, Acc) -> escape(T, <>); escape(<<19, T/binary>>, Acc) -> escape(T, <>); escape(<<20, T/binary>>, Acc) -> escape(T, <>); escape(<<21, T/binary>>, Acc) -> escape(T, <>); escape(<<22, T/binary>>, Acc) -> escape(T, <>); escape(<<23, T/binary>>, Acc) -> escape(T, <>); escape(<<24, T/binary>>, Acc) -> escape(T, <>); escape(<<25, T/binary>>, Acc) -> escape(T, <>); escape(<<26, T/binary>>, Acc) -> escape(T, <>); escape(<<27, T/binary>>, Acc) -> escape(T, <>); escape(<<28, T/binary>>, Acc) -> escape(T, <>); escape(<<29, T/binary>>, Acc) -> escape(T, <>); escape(<<30, T/binary>>, Acc) -> escape(T, <>); escape(<<31, T/binary>>, Acc) -> escape(T, <>); escape(<<$", T/binary>>, Acc) -> escape(T, <>); escape(<<$\/, T/binary>>, Acc) -> escape(T, <>); escape(<<$\\, T/binary>>, Acc) -> escape(T, <>); escape(<>, Acc) -> escape(T, <>). %% =================================================================== %% Decoding %% =================================================================== do_decode(<<>>, true, S) -> {more, {decode, S}}; do_decode(<>, F, S) -> do_decode(T, F, S); do_decode(<>, F, S) -> do_decode(T, F, S); do_decode(<>, F, S) -> do_decode(T, F, S); do_decode(<>, F, S) -> do_decode(T, F, S); do_decode(<<$t, T/binary>>, F, S) -> base("rue", T, true, F, S); do_decode(<<$f, T/binary>>, F, S) -> base("alse", T, false, F, S); do_decode(<<$n, T/binary>>, F, S) -> base("ull", T, null, F, S); do_decode(<<${, T/binary>>, F, S) -> object(T, {true, name}, [], F, S); do_decode(<<$[, T/binary>>, F, S) -> array(T, {false, false}, [], F, S); do_decode(<<$", T/binary>>, F, S) -> string(T, <<>>, F, S); do_decode(<<$-, T/binary>>, F, S) -> number(T, pre, int, [$-], F, S); do_decode(<<$/>>, false, _) -> [<<>>]; do_decode(<<$/, T/binary>>, false, _) -> pointer(T, []); do_decode(B = <<$0, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$1, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$2, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$3, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$4, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$5, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$6, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$7, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$8, _/binary>>, F, S) -> number(B, pre, int, [], F, S); do_decode(B = <<$9, _/binary>>, F, S) -> number(B, pre, int, [], F, S). base("", T, V, F, S) -> pop(V, T, F, S); base(Check, <<>>, V, true, S) -> {more, {base, Check, V, S}}; base([H | Check], <>, V, F, S) -> base(Check, T, V, F, S). object(<<>>, State, Acc, true, S) -> {more, {object, State, Acc, S}}; object(<>, State, Acc, F, S) -> object(T, State, Acc, F, S); object(<>, State, Acc, F, S) -> object(T, State, Acc, F, S); object(<>, State, Acc, F, S) -> object(T, State, Acc, F, S); object(<>, State, Acc,F, S) -> object(T, State, Acc, F, S); object(<<$}, T/binary>>, {true,_}, Acc, F, S) -> pop(maps:from_list(Acc),T,F,S); object(<<$,, T/binary>>,{true,comma},Acc,F,S) -> object(T,{false,name},Acc,F,S); object(<<$", T/binary>>, {_, name},Acc,F,S) -> string(T,<<>>,F,[{name, Acc}|S]); object(<<$:, T/binary>>, {false, colon}, Acc, F, S) -> do_decode(T, F, [{value,Acc}|S]). array(<<>>, State, Acc, true, S) -> {more, {array, State, Acc, S}}; array(<>, State, Acc, F, S) -> array(T, State, Acc, F, S); array(<>, State, Acc, F, S) -> array(T, State, Acc, F, S); array(<>, State, Acc, F, S) -> array(T, State, Acc, F, S); array(<>, State, Acc, F, S) -> array(T, State, Acc, F, S); array(<<$,, T/binary>>, {false, true},Acc,F,S) -> array(T,{true,false},Acc,F,S); array(<<$], T/binary>>, {false, _}, Acc, F,S) -> pop(lists:reverse(Acc), T,F,S); array(T, {_, false}, Acc, F, S) -> do_decode(T, F, [{array, Acc} | S]). string(<<>>, Acc, true, S) -> {more, {string, Acc, S}}; string(<<$\\, T/binary>>, Acc, F, S) -> unescape(T, Acc, F, S); string(<<$", T/binary>>, Acc, F, S) -> pop(Acc, T, F, S); string(<>, Acc, F, S) -> string(T, <>,F,S). unescape(<<>>, Acc, true, S) -> {more, {unescape, Acc, S}}; unescape(<<$", T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$\\, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$/, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$0, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$a, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$b, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$t, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$n, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$f, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$v, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$r, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$s, T/binary>>, Acc, F, S) -> string(T, <>, F, S); unescape(<<$u, T/binary>>, Acc, F, S) -> unescape_hex(T, [], Acc, F, S); unescape(<>, Acc, F, S) -> string(T, <>, F, S). unescape_hex(<<>>, Hex, Acc, true, S) -> {more, {unescape_hex, Hex, Acc, S}}; unescape_hex(<>, [C, B, A], Acc, F, S) -> string(T, <>, F, S); unescape_hex(<>, Hex, Acc, F, S) -> unescape_hex(T, [X | Hex], Acc, F, S). number(<<$0, T/binary>>, pre, int, Acc, F, S) -> number(T, zero, int, [$0|Acc], F, S); number(<>, pre, exp, Acc, F, S) when ?IS_SIGN(H) -> number(T, sign, exp, [H | Acc], F, S); number(<>, pre, exp, Acc, F, S) when ?IS_INT(H) -> number(T, post, exp, [H | Acc], F, S); number(<>, pre, float, Acc, F, S) when ?IS_INT(H) -> number(T, post, float, [H | Acc], F, S); number(<>, pre, Phase, Acc, F, S) when ?IS_POS_INT(H) -> number(T, post, Phase, [H | Acc], F, S); number(<>, sign, Phase, Acc, F, S) when ?IS_INT(H) -> number(T, post, Phase, [H | Acc], F, S); number(<>, post, Phase, Acc, F, S) when ?IS_INT(H) -> number(T, post, Phase, [H | Acc], F, S); number(<<$., T/binary>>, Stage, int, Acc,F, S) when ?ZERO_OR_POST(Stage) -> number(T, pre, float, [$. | Acc], F, S); number(<>,Stage, int, Acc, F, S) when ?EXP_ZERO_OR_POST(E, Stage) -> number(T, pre, exp, [E, $0, $. | Acc], F, S); number(<>, post, float, Acc, F, S) when ?IS_EXP(E) -> number(T, pre, exp, [E | Acc], F, S); number(<<>>, State, Phase, Acc, true, S) -> {more, {number, State, Phase, Acc, S}}; number(B, Stage, int, Acc, F, S) when ?ZERO_OR_POST(Stage) -> pop(list_to_integer(lists:reverse(Acc)), B, F, S); number(B, post, _, Acc, F, S) -> pop(list_to_float(lists:reverse(Acc)), B, F, S). pointer(<<>>, Acc) -> lists:reverse(Acc); pointer(<<$->>, Acc) -> lists:reverse(['-'| Acc]); pointer(<<$-, $/, T/binary>>, Acc) -> pointer(T, ['-' | Acc]); pointer(<<$0, T/binary>>, Acc) -> pointer_int(T, Acc, [$0]); pointer(<<$1, T/binary>>, Acc) -> pointer_int(T, Acc, [$1]); pointer(<<$2, T/binary>>, Acc) -> pointer_int(T, Acc, [$2]); pointer(<<$3, T/binary>>, Acc) -> pointer_int(T, Acc, [$3]); pointer(<<$4, T/binary>>, Acc) -> pointer_int(T, Acc, [$4]); pointer(<<$5, T/binary>>, Acc) -> pointer_int(T, Acc, [$5]); pointer(<<$6, T/binary>>, Acc) -> pointer_int(T, Acc, [$6]); pointer(<<$7, T/binary>>, Acc) -> pointer_int(T, Acc, [$7]); pointer(<<$8, T/binary>>, Acc) -> pointer_int(T, Acc, [$8]); pointer(<<$9, T/binary>>, Acc) -> pointer_int(T, Acc, [$9]); pointer(<>, Acc) -> pointer_member(T, Acc, <>). pointer_int(<<>>, Pointer, Acc) -> lists:reverse([list_to_integer(lists:reverse(Acc)) | Pointer]); pointer_int(<<$/, T/binary>>, Pointer, Acc) -> pointer(T, [list_to_integer(Acc) | Pointer]); pointer_int(<>, Pointer, Acc) -> pointer_int(T, Pointer, [H | Acc]). pointer_member(<<>>, Pointer, Acc) -> lists:reverse([Acc | Pointer]); pointer_member(<<$/, T/binary>>, Pointer, Acc) -> pointer(T, [Acc| Pointer]); pointer_member(<<$~, $0, T/binary>>, Pointer, Acc) -> pointer_member(T, Pointer, <>); pointer_member(<<$~, $1, T/binary>>, Pointer, Acc) -> pointer_member(T, Pointer, <>); pointer_member(<>, Pointer, Acc) -> pointer_member(T, Pointer, <>). pop(V, B, _, []) -> {V, B}; pop(V, B, F, [{array, Acc} | S]) -> array(B, {false, true}, [V | Acc], F, S); pop(N, B, F, [{name, Acc} | S]) -> object(B, {false, colon}, {N, Acc}, F, S); pop(V, B, F, [{value, {N, Acc}} | S]) -> object(B, {true,comma},[{N, V} | Acc], F, S). %% =================================================================== %% Pointer Evaluation %% =================================================================== eval_binary([], B, false, _) -> decode(B); eval_binary([], B, true, Path) -> case decode(B, [stream]) of {more, Cont} -> {more, {decode, Path, Cont}}; {JSON, T} -> unwind(Path, T, JSON) end; eval_binary(P, <<>>, Stream, Path) -> {more, {eval_binary, P, Stream, Path}}; eval_binary(P, <>, Stream, Path) when ?WS(W) -> eval_binary(P, T, Stream, Path); eval_binary(['-' | _], <<$[, T/binary>>, Stream, Path) -> eval_dash(T, {false, false}, 0, Stream, Path); eval_binary(['-' | _], _, _, Path) -> {error, incorrect(['-' | Path])}; eval_binary([N | R], <<$[, T/binary>>, Stream, Path) when is_integer(N) -> eval_array(N, T, {false, false}, Stream, [N | Path], R); eval_binary([Key | P], B = <<${, _/binary>>, Stream, Path) when is_atom(Key) -> eval_binary([atom_to_binary(Key, utf8) | P], B, Stream, Path); eval_binary([Key | P], <<${, T/binary>>, Stream,Path) when is_binary(Key) -> eval_object(Key, T, {false, false}, Stream, [Key | Path], P); eval_binary([E | _], _, _, Path) -> {error, incorrect([E | Path])}. eval_dash(<<>>, Expect, Size, Stream, Path) -> {more, {eval_dash, Expect, Size, Stream, Path}}; eval_dash(<>, Expect, Size, Stream, Path) when ?WS(W) -> eval_dash(T, Expect, Size, Stream, Path); eval_dash(<<$,, T/binary>>, {false, true}, Size, Stream, Path) -> eval_dash(T, {true, false}, Size, Stream, Path); eval_dash(<<$], _/binary>>, {false, _}, Size, _, Path) -> {error, too_large([Size | Path])}; eval_dash(T, {_, false}, Size, Stream, Path) -> skip_value(T, {eval_dash, {false, true}, Size + 1, Stream, Path}). eval_array(0, T, _, Stream, Path, Rest) -> eval_binary(Rest, T, Stream, Path); eval_array(N, <<>>, Expect, Stream, Path, Rest) -> {more, {eval_array, N, Expect, Stream, Path, Rest}}; eval_array(N, <>, Expect, Stream, Path, Rest) when ?WS(W) -> eval_array(N, T, Expect, Stream, Path, Rest); eval_array(N, <<$,, T/binary>>, {false, true}, Stream, Path, Rest) -> eval_array(N - 1, T, {true, false}, Stream, Path, Rest); eval_array(_, <<$], _/binary>>, {false, _}, _, Path, _) -> {error, too_large(Path)}; eval_array(N, T, {_, false}, Stream, Path, Rest) -> skip_value(T, {eval_array, N, {false, true}, Stream, Path, Rest}). eval_object(Key, <<>>, Expect, Stream, Path, Rest) -> {more, {eval_object, Key, Expect, Stream, Path, Rest}}; eval_object(Key, <>, Expect, Stream, Path, Rest) when ?WS(W)-> eval_object(Key, T, Expect, Stream, Path, Rest); eval_object(_, <<$}, _/binary>>, {false, _}, _, Path, _) -> {error, non_member(Path)}; eval_object(Key, <<$,, T/binary>>, {false, true}, Stream, Path, Rest) -> eval_object(Key, T, {true, false}, Stream, Path, Rest); eval_object(Key, <<$", T/binary>>, {_, false}, Stream, Path, Rest) -> eval_key(T, Key, Stream, Path, Rest). eval_key(<<>>, Key, Stream,Path,Rest) -> {more,{eval_key,Key,Stream,Path,Rest}}; eval_key(<<$", T/binary>>, <<>>, Stream, Path, Rest) -> skip_colon(T, false, {eval_binary, Rest, Stream, Path}); eval_key(<>, <>, Stream, Path, Rest) -> eval_key(T, Key, Stream, Path, Rest); eval_key(T, _, Stream, Path = [Key | _], Rest) -> skip_colon(T, false, {skip_value, {eval_object, Key, {false,true},Stream,Path,Rest}}). skip_value(<<>>, C) -> {more, {skip_value, C}}; skip_value(<>, C) when ?WS(W) -> skip_value(T, C); skip_value(<<$t, T/binary>>, C) -> skip_base("rue", T, C); skip_value(<<$f, T/binary>>, C) -> skip_base("alse", T, C); skip_value(<<$n, T/binary>>, C) -> skip_base("ull", T, C); skip_value(<<${, T/binary>>, C) -> skip_object(T, {false, false}, C); skip_value(<<$[, T/binary>>, C) -> skip_array(T, {false, false}, C); skip_value(<<$", T/binary>>, C) -> skip_string(T, C); skip_value(<<$-, T/binary>>, C) -> skip_number(T, C); skip_value(<>, C) when ?IS_INT(H) -> skip_number(T, C). skip_base(Base, <<>>, C) -> {more, {skip_base, Base, C}}; skip_base("", T, C) -> skip_cont(T, C); skip_base([H | T], <>, C) -> skip_base(T, T1, C). skip_array(<<>>, Expect, C) -> {more, {skip_array, Expect, C}}; skip_array(<>, Expect, C) when ?WS(W) -> skip_array(T,Expect,C); skip_array(<<$,, T/binary>>, {false, true}, C) -> skip_array(T, {true,false},C); skip_array(<<$], T/binary>>, {false, _}, C) -> skip_cont(T, C); skip_array(T, {_, false}, C) -> skip_value(T, {skip_array, {false, true}, C}). skip_object(<<>>, Expect, C) -> {more, {skip_object, Expect, C}}; skip_object(<>, Expect, C) when ?WS(W) -> skip_object(T, Expect,C); skip_object(<<$}, T/binary>>, {false, _}, C) -> skip_cont(T, C); skip_object(<<$,, T/binary>>, {false, true},C) -> skip_object(T,{true,false},C); skip_object(<<$", T/binary>>, {_, false}, C) -> skip_name(T, {skip_object, {false, true}, C}). skip_name(<<>>, C) -> {more, {skip_name, C}}; skip_name(<<$", T/binary>>, C) -> skip_colon(T, false, {skip_value, C}); skip_name(<<_/utf8, T/binary>>, C) -> skip_name(T, C). skip_colon(<<>>, Flag, C) -> {more, {skip_colon, Flag, C}}; skip_colon(<>, Flag, C) when ?WS(W) -> skip_colon(T, Flag, C); skip_colon(T, true, {eval_binary, Rest, S, Path}) -> eval_binary(Rest,T,S,Path); skip_colon(T, true, {skip_value, C}) -> skip_value(T, C); skip_colon(<<$:, T/binary>>, false, C) -> skip_colon(T, true, C); skip_colon(<<_/utf8, T/binary>>, false, C) -> skip_colon(T, false, C). skip_string(<<>>, C) -> {more, {skip_string, C}}; skip_string(<<$", T/binary>>, C) -> skip_cont(T, C); skip_string(<<_/utf8, T/binary>>, C) -> skip_string(T, C). skip_number(<<>>, C) -> {more, {skip_number, C}}; skip_number(<<$0, T/binary>>, C) -> skip_number(T, C); skip_number(<<$1, T/binary>>, C) -> skip_number(T, C); skip_number(<<$2, T/binary>>, C) -> skip_number(T, C); skip_number(<<$3, T/binary>>, C) -> skip_number(T, C); skip_number(<<$4, T/binary>>, C) -> skip_number(T, C); skip_number(<<$5, T/binary>>, C) -> skip_number(T, C); skip_number(<<$6, T/binary>>, C) -> skip_number(T, C); skip_number(<<$7, T/binary>>, C) -> skip_number(T, C); skip_number(<<$8, T/binary>>, C) -> skip_number(T, C); skip_number(<<$9, T/binary>>, C) -> skip_number(T, C); skip_number(<<$e, T/binary>>, C) -> skip_number(T, C); skip_number(<<$E, T/binary>>, C) -> skip_number(T, C); skip_number(<<$-, T/binary>>, C) -> skip_number(T, C); skip_number(<<$+, T/binary>>, C) -> skip_number(T, C); skip_number(<<$., T/binary>>, C) -> skip_number(T, C); skip_number(T = <<_/utf8, _/binary>>, C) -> skip_cont(T, C). skip_cont(T, {eval_binary, Rest, Stream, Path}) -> eval_binary(Rest, T, Stream, Path); skip_cont(T, {eval_dash, Expect, Size, Stream, Path}) -> eval_dash(T, Expect,Size, Stream, Path); skip_cont(T, {eval_array, N, Expect, Stream, Path, Rest}) -> eval_array(N, T, Expect, Stream, Path, Rest); skip_cont(T, {eval_object, Key, Expect, Stream, Path, Rest}) -> eval_object(Key, T, Expect, Stream, Path, Rest); skip_cont(T, {skip_object, Expect, C}) -> skip_object(T, Expect, C); skip_cont(T, {skip_array, Expect, C}) -> skip_array(T, Expect, C); skip_cont(T, {skip_value, C}) -> skip_value(T, C). unwind([], T, JSON) -> {JSON, T}; unwind(P, <<>>, JSON) -> {more, {unwind, P, JSON}}; unwind([N | P], T, JSON) when is_integer(N) -> unwind_array(T, {unwind, P, JSON}); unwind([_ | P], T, JSON) -> unwind_object(T, {unwind, P, JSON}). unwind_array(<<>>, C) -> {more, {unwind_array, C}}; unwind_array(<>, C) when ?WS(W) -> unwind_array(T, C); unwind_array(<<$,, T/binary>>, C) -> unwind_value(T, {unwind_array, C}); unwind_array(<<$], T/binary>>, C) -> unwind_cont(T, C); unwind_array(T, C) -> unwind_value(T, {unwind_array, C}). unwind_object(<<>>, C) -> {more, {unwind_object, C}}; unwind_object(<>, C) when ?WS(W) -> unwind_object(T, C); unwind_object(<<$,, T/binary>>, C) -> unwind_object(T, C); unwind_object(<<$", T/binary>>, C) -> unwind_name(T, C); unwind_object(<<$}, T/binary>>, C) -> unwind_cont(T, C). unwind_name(<<>>, C) -> {more, {unwind_name, C}}; unwind_name(<>, C) when ?WS(W) -> unwind_name(T, C); unwind_name(<<$", T/binary>>, C) -> unwind_colon(T, C); unwind_name(<<_/utf8, T/binary>>, C) -> unwind_name(T, C). unwind_colon(<<>>, C) -> {more, {unwind_colon, C}}; unwind_colon(<>, C) when ?WS(W) -> unwind_colon(T, C); unwind_colon(<<$:, T/binary>>,C) -> unwind_value(T, {unwind_object, C}). unwind_string(<<>>, C) -> {more, {unwind_string, C}}; unwind_string(<>, C) when ?WS(W) -> unwind_string(T, C); unwind_string(<<$", T/binary>>,C) -> unwind_cont(T, C); unwind_string(<<_/utf8, T/binary>>, C) -> unwind_string(T, C). unwind_value(<<>>, C) -> {more, {unwind_value, C}}; unwind_value(<>, C) when ?WS(W) -> unwind_value(T, C); unwind_value(<<$t, T/binary>>, C) -> unwind_base("rue", T, C); unwind_value(<<$f, T/binary>>, C) -> unwind_base("alse", T, C); unwind_value(<<$n, T/binary>>, C) -> unwind_base("ull", T, C); unwind_value(<<${, T/binary>>, C) -> unwind_object(T, C); unwind_value(<<$[, T/binary>>, C) -> unwind_array(T, C); unwind_value(<<$", T/binary>>, C) -> unwind_string(T, C); unwind_value(<<$-, T/binary>>, C) -> unwind_number(T, C); unwind_value(<>, C) when ?IS_INT(H) -> unwind_number(T, C). unwind_number(<<>>, C) -> {more, {unwind_number, C}}; unwind_number(<<$0, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$1, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$2, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$3, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$4, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$5, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$6, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$7, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$8, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$9, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$e, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$E, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$-, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$+, T/binary>>, C) -> unwind_number(T, C); unwind_number(<<$., T/binary>>, C) -> unwind_number(T, C); unwind_number(T = <<_/utf8, _/binary>>, C) -> unwind_cont(T, C). unwind_base(Expect, <<>>, C) -> {more, {unwind_base, Expect, C}}; unwind_base([], T, C) -> unwind_cont(T, C); unwind_base([H | B], <>, C) -> unwind_base(B, T, C). unwind_cont(T, {unwind, Path, JSON}) -> unwind(Path, T, JSON); unwind_cont(T, {unwind_array, C}) -> unwind_array(T, C); unwind_cont(T, {unwind_object, C}) -> unwind_object(T, C). eval_json([], JSON, _) -> JSON; eval_json(['-' | _], JSON, Path) when is_list(JSON) -> {error, too_large([length(JSON) | Path])}; eval_json(['-' | _], _, Path) -> {error, incorrect(['-' | Path])}; eval_json([N | T], JSON, Path) when is_integer(N), length(JSON) > N -> eval_json(T, lists:nth(N + 1, JSON), [N | Path]); eval_json([N | _], _, Path) when is_integer(N) -> {error, too_large([N | Path])}; eval_json([Key | T], JSON, Path) -> case maps:find(Key, JSON) of {ok, Value} -> eval_json(T, Value, [Key | Path]); _ -> {error, non_member([Key | Path])} end; eval_json(X, _, Path) -> {error, incorrect([X | Path])}. incorrect(Path) -> {incorrect_pointer, encode(lists:reverse(Path), [pointer, binary])}. too_large(Path) -> {too_large_index, encode(lists:reverse(Path), [pointer, binary])}. non_member(Path) -> {non_member, encode(lists:reverse(Path), [pointer, binary])}. %% =================================================================== %% Next %% =================================================================== do_next(T) -> next_value(T, <<>>, {next_end}). next_value(<<>>, Acc, C) -> {more, {next_value, Acc, C}}; next_value(<>, Acc, C) when ?WS(W) -> next_value(T, Acc, C); next_value(<<$t, T/binary>>, Acc, C) -> next_base("rue", T, <>, C); next_value(<<$f, T/binary>>, Acc, C) -> next_base("alse", T, <>, C); next_value(<<$n, T/binary>>, Acc, C) -> next_base("ull", T, <>, C); next_value(<<${, T/binary>>, Acc, C) -> next_object(T, {false, false}, <>, C); next_value(<<$[, T/binary>>, Acc, C) -> next_array(T, {false, false}, <>, C); next_value(<<$", T/binary>>, Acc, C) -> next_string(T, <>, C); next_value(<<$-, T/binary>>, Acc, C) -> next_number(T, <>, C); next_value(<>, Acc, C) when ?IS_INT(H) -> next_number(T, <>, C). next_base("", T, Acc, C) -> next_cont(T, Acc, C); next_base(Base, <<>>, Acc, C) -> {more, {next_base, Base, Acc, C}}; next_base([H | T], <>, Acc, C) -> next_base(T, T1, Acc, C). next_array(<<>>, Expect, Acc, C) -> {more, {next_array, Expect, Acc, C}}; next_array(<>, Expect, Acc, C) when ?WS(W) -> next_array(T, Expect, Acc, C); next_array(<<$,, T/binary>>, {false, true}, Acc, C) -> next_array(T, {true, false}, <>, C); next_array(<<$], T/binary>>, {false, _}, Acc, C) -> next_cont(T, <>, C); next_array(T, {_, false}, Acc, C) -> next_value(T, Acc, {next_array, {false, true}, C}). next_object(<<>>, Expect, Acc, C) -> {more, {next_object, Expect, Acc, C}}; next_object(<>, Expect, Acc, C) when ?WS(W) -> next_object(T, Expect, Acc, C); next_object(<<$}, T/binary>>, {false, _}, Acc, C) -> next_cont(T, <>, C); next_object(<<$,, T/binary>>, {false, true}, Acc, C) -> next_object(T, {true,false}, <>, C); next_object(<<$", T/binary>>, {_, false}, Acc, C) -> next_name(T, <>, {next_object, {false, true}, C}). next_name(<<>>, Acc, C) -> {more, {next_name, Acc, C}}; next_name(<<$", T/binary>>, Acc, C) -> next_colon(T, false, <>, C); next_name(<>, Acc, C) -> next_name(T, <>, C). next_colon(<<>>, Flag, Acc, C) -> {more, {next_colon, Flag, Acc, C}}; next_colon(<>, Flag, Acc, C) when ?WS(W) -> next_colon(T, Flag, Acc, C); next_colon(<<$:, T/binary>>, false, Acc, C) -> next_colon(T, true, <>, C); next_colon(T = <<_/utf8, _/binary>>, true, Acc, C) -> next_value(T, Acc, C). next_string(<<>>, Acc, C) -> {more, {next_string, Acc, C}}; next_string(<<$", T/binary>>, Acc, C) -> next_cont(T, <>, C); next_string(<<$\\, T/binary>>, Acc, C) -> next_unescape(T, <>, C); next_string(<>, Acc, C) -> next_string(T, <>, C). next_unescape(<<>>, Acc, C) -> {more, {next_unescape, Acc, C}}; next_unescape(<<$", T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$\\, T/binary>>, Acc, C) -> next_string(T,<>,C); next_unescape(<<$/, T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$0, T/binary>>, Acc,C) -> next_string(T,<>, C); next_unescape(<<$a, T/binary>>, Acc, C) -> next_string(T,<>, C); next_unescape(<<$b, T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$t, T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$n, T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$f, T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$v, T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$r, T/binary>>, Acc, C) -> next_string(T, <>,C); next_unescape(<<$s, T/binary>>, Acc, C) -> next_string(T,<>, C); next_unescape(<<$u, T/binary>>, Acc, C) -> next_unescape_hex(T, [], Acc, C); next_unescape(<>, Acc, C) -> next_string(T,<>,C). next_unescape_hex(<<>>, Hex, Acc, C) -> {more,{next_unescape_hex, Hex, Acc, C}}; next_unescape_hex(<>, [C, B, A], Acc, Co) -> next_string(T, <>, Co); next_unescape_hex(<>, Hex, Acc, C) -> next_unescape_hex(T, [X | Hex], Acc, C). next_number(<<>>, Acc, C) -> {more, {next_number, Acc, C}}; next_number(<<$0, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$1, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$2, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$3, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$4, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$5, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$6, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$7, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$8, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$9, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$e, T/binary>>, Acc, C) -> next_number(T, <> ,C); next_number(<<$E, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$-, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$+, T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(<<$., T/binary>>, Acc, C) -> next_number(T, <>, C); next_number(T = <<_/utf8, _/binary>>, Acc, C) -> next_cont(T, Acc, C). next_cont(T, Acc, {next_end}) -> {Acc, T}; next_cont(T, Acc, {next_array, Expect, C}) -> next_array(T, Expect, Acc, C); next_cont(T, Acc, {next_object, Expect, C}) -> next_object(T, Expect, Acc, C); next_cont(T, Acc, {next_value, C}) -> next_value(T, Acc, C). %% =================================================================== %% Patch %% =================================================================== apply_op(#{<<"op">> := <<"add">>, <<"path">> := P, <<"value">> := V}, JSON) -> add(decode(P), JSON, V); apply_op(#{<<"op">> := <<"remove">>, <<"path">> := P}, JSON) -> remove(decode(P), JSON); apply_op(#{<<"op">> := <<"replace">>, <<"path">> := P,<<"value">> := V},JSON) -> replace(decode(P), JSON, V); apply_op(#{<<"op">> := <<"move">>, <<"path">> := P, <<"from">> := F}, JSON) -> move(decode(P), JSON, decode(F)); apply_op(#{<<"op">> := <<"copy">>, <<"path">> := P, <<"from">> := F}, JSON) -> copy(decode(P), JSON, decode(F)); apply_op(#{<<"op">> := <<"test">>, <<"path">> := P, <<"value">> := V}, JSON) -> test(decode(P), JSON, V). add(['-'], [], V) -> [V]; add(['-'], Array = [_ | _], V) -> lists:reverse([V | lists:reverse(Array)]); add([Key], Object = #{}, V) -> Object#{Key => V}; add([N], Array, V) when is_integer(N) -> add_array(N, Array, V); add([Key | P], Object = #{}, V) -> Value = maps:get(Key, Object), Object#{Key => add(P, Value, V)}; add([N | P], Array, V) -> add_array(N, Array, V, P). add_array(0, Array = [_ | _], V) -> [V | Array]; add_array(N, [H | T], V) -> [H | add_array(N - 1, T, V)]. add_array(0, [H | T], V, P) -> [add(P, H, V) | T]; add_array(N, [H | T], V, P) -> [H | add_array(N - 1, T, V, P)]. remove([Key], Object = #{}) -> _ = maps:get(Key, Object), maps:remove(Key, Object); remove([N], Array) when is_integer(N) -> remove_array(N, Array); remove([Key | P], Object = #{}) -> Value = maps:get(Key, Object), Object#{Key => remove(P, Value)}; remove([N | P], Array) when is_integer(N) -> remove_array(N, Array, P). remove_array(0, [_ | T]) -> T; remove_array(N, [H | T]) -> [H | remove_array(N - 1, T)]. remove_array(0, [H | T], P) -> [remove(P, H) | T]; remove_array(N, [H | T], P) -> [H | remove_array(N - 1, T, P)]. replace([Key], Object = #{}, V) -> _ = maps:get(Key, Object), Object#{Key => V}; replace([N], Array, V) when is_integer(N) -> replace_array(N, Array, V); replace([Key | P], O = #{}, V) -> Value = maps:get(Key, O), O#{Key => replace(P, Value, V)}; replace([N | P], Array, V) -> replace_array(N, Array, V, P). replace_array(0, [_ | T], V) -> [V | T]; replace_array(N, [H | T], V) -> [H | replace_array(N - 1, T, V)]. replace_array(0, [H | T], V, P) -> [replace(P, H, V) | T]; replace_array(N, [H | T], V, P) -> [H | replace_array(N - 1, T, V, P)]. move(P, JSON, From) -> {Value, JSON1} = fetch(From, JSON), add(P, JSON1, Value). fetch([Key], Object = #{}) -> V = maps:get(Key, Object), {V, maps:remove(Key, Object)}; fetch([N], Array) when is_integer(N) -> fetch_array(N, Array); fetch([Key | P], Object = #{}) -> Value = maps:get(Key, Object), Object#{Key => fetch(P, Value)}; fetch([N | P], Array) when is_integer(N) -> fetch_array(N, Array, P). fetch_array(0, [H | T]) -> [H | T]; fetch_array(N, [H | T]) -> [H | fetch_array(N - 1, T)]. fetch_array(0, [H | T], P) -> [fetch(P, H) | T]; fetch_array(N, [H | T], P) -> [H | fetch_array(N - 1, T, P)]. copy(P, JSON, From) -> add(P, JSON, eval_json(From, JSON, [])). test([Key], Object = #{}, Value) -> case maps:get(Key, Object, undefined) of Value -> true; _ -> false end; test([N], A, V) when is_integer(N), length(A) > N -> V == lists:nth(N, A); test([_], _, _) -> false; test([Key | P], Object = #{}, Value) -> case maps:get(Key, Object) of undefined -> false; T -> test(P, T, Value) end; test([N | P], A, V) when is_integer(N), length(A) > N -> test(P,lists:nth(N, A),V); test(_, _, _) -> false. %% =================================================================== %% Merge %% =================================================================== merge_object(Key, null, Object) -> maps:remove(Key, Object); merge_object(K, PV, O = #{}) -> O#{K => merge(PV, maps:get(K, O))}.