You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ei...@apache.org on 2020/02/11 16:32:16 UTC
[couchdb] 01/04: Add JSON explode/implode function
This is an automated email from the ASF dual-hosted git repository.
eiri pushed a commit to branch prototype/flattened-doc-storage
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit ba9a54836cf045588583a6c77445fb2c81eb9756
Author: Eric Avdey <ei...@eiri.ca>
AuthorDate: Fri Feb 7 12:00:45 2020 -0400
Add JSON explode/implode function
---
src/couch/src/couch_util.erl | 71 ++++++++++++++++++++++
src/couch/test/eunit/couch_util_tests.erl | 99 +++++++++++++++++++++++++++++++
2 files changed, 170 insertions(+)
diff --git a/src/couch/src/couch_util.erl b/src/couch/src/couch_util.erl
index 260aac6..ad810a1 100644
--- a/src/couch/src/couch_util.erl
+++ b/src/couch/src/couch_util.erl
@@ -22,6 +22,7 @@
-export([proplist_apply_field/2, json_apply_field/2]).
-export([to_binary/1, to_integer/1, to_list/1, url_encode/1]).
-export([json_encode/1, json_decode/1]).
+-export([json_explode/1, json_implode/1]).
-export([verify/2,simple_call/2,shutdown_sync/1]).
-export([get_value/2, get_value/3]).
-export([reorder_results/2]).
@@ -515,6 +516,76 @@ json_decode(V) ->
throw({invalid_json, Error})
end.
+
+json_explode({[]}) ->
+ {};
+json_explode({V}) ->
+ json_explode(V);
+json_explode(V) when is_list(V), length(V) > 0 ->
+ json_explode(V, []);
+json_explode(V) ->
+ V.
+
+json_explode([], Acc) ->
+ lists:reverse(Acc);
+json_explode([{K, V} | Rest], Acc) ->
+ Acc1 = json_explode(V, [K], []),
+ json_explode(Rest, lists:append(Acc1, Acc));
+json_explode(L, Acc) when is_list(L) ->
+ {_, Acc1} = lists:foldl(fun(V, {K, A}) ->
+ A1 = json_explode(V, [K], []),
+ {K+1, lists:append(A1, A)}
+ end, {0, Acc}, L),
+ lists:reverse(Acc1).
+
+json_explode({[]}, KAcc, []) ->
+ [{lists:reverse(KAcc), {}}];
+json_explode({[]}, _KAcc, Acc) ->
+ Acc;
+json_explode({[{K, V} | Rest]}, KAcc, Acc) ->
+ Acc1 = json_explode(V, [K | KAcc], Acc),
+ json_explode({Rest}, KAcc, Acc1);
+json_explode(L, KAcc, Acc) when is_list(L), length(L) > 0 ->
+ {_, Acc1} = lists:foldl(fun(V, {K, A}) ->
+ A1 = json_explode(V, [K | KAcc], []),
+ {K+1, lists:append(A1, A)}
+ end, {0, Acc}, L),
+ Acc1;
+json_explode(V, KAcc, Acc) ->
+ V1 = {lists:reverse(KAcc), V},
+ [V1 | Acc].
+
+
+json_implode([{[K | _], _} | _] = L) ->
+ V = json_implode(L, []),
+ if is_number(K) -> V; true -> {V} end;
+json_implode({}) ->
+ {[]};
+json_implode(V) ->
+ V.
+
+json_implode([], Acc) ->
+ lists:reverse(Acc);
+json_implode([{[K1 | K2] = K, V} | Rest], Acc) ->
+ {V1, Rest1} = json_implode(K, V, Rest),
+ %% try to put it at the end of Rest1?
+ V2 = json_implode(V1),
+ V3 = if is_number(K1) -> V2; true -> {K1, V2} end,
+ json_implode(Rest1, [V3 | Acc]).
+
+json_implode([_K], V, Rest) ->
+ {V, Rest};
+json_implode([K1 | K2], V, Rest) ->
+ json_implode(K1, Rest, [{K2, V}], []).
+
+json_implode(_K, [], KAcc, RestAcc) ->
+ {lists:reverse(KAcc), lists:reverse(RestAcc)};
+json_implode(K, [{[K | K1], V} | Rest], KAcc, RestAcc) ->
+ json_implode(K, Rest, [{K1, V} | KAcc], RestAcc);
+json_implode(K, [V | Rest], KAcc, RestAcc) ->
+ json_implode(K, Rest, KAcc, [V | RestAcc]).
+
+
verify([X|RestX], [Y|RestY], Result) ->
verify(RestX, RestY, (X bxor Y) bor Result);
verify([], [], Result) ->
diff --git a/src/couch/test/eunit/couch_util_tests.erl b/src/couch/test/eunit/couch_util_tests.erl
index b518028..97df15e 100644
--- a/src/couch/test/eunit/couch_util_tests.erl
+++ b/src/couch/test/eunit/couch_util_tests.erl
@@ -308,3 +308,102 @@ random_unicode_char() ->
BC ->
BC
end.
+
+
+json_explode_test_() ->
+ {
+ setup,
+ fun explode_implode_setup/0,
+ fun(Cases) ->
+ lists:map(fun({Description, Before0, After}) ->
+ Before = couch_util:json_decode(Before0),
+ {Description,
+ ?_assertEqual(After, couch_util:json_explode(Before))}
+ end, Cases)
+ end
+ }.
+
+json_implode_test_() ->
+ {
+ setup,
+ fun explode_implode_setup/0,
+ fun(Cases) ->
+ lists:map(fun({Description, Before0, After}) ->
+ Before = couch_util:json_decode(Before0),
+ {Description,
+ ?_assertEqual(Before, couch_util:json_implode(After))}
+ end, Cases)
+ end
+ }.
+
+explode_implode_setup() ->
+ [
+ {"null", <<"null">>, null},
+ {"integer", <<"1">>, 1},
+ {"bool", <<"true">>, true},
+ {"string", <<"\"a\"">>, <<"a">>},
+ {"empty list", <<"[]">>, []},
+ {"one element list", <<"[\"a\"]">>, [{[0], <<"a">>}]},
+ {"two elements list", <<"[\"a\", 1]">>,
+ [{[0], <<"a">>}, {[1], 1}]},
+ {"list of lists", <<"[[\"a\", \"b\"], [1, 2, 3], [true]]">>,
+ [{[0, 0], <<"a">>}, {[0, 1], <<"b">>},
+ {[1, 0], 1}, {[1, 1], 2}, {[1, 2], 3}, {[2, 0], true}]},
+ {"deep list of lists", <<"[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]">>,
+ [{[0, 0, 0], 1}, {[0, 0, 1], 2}, {[0, 1, 0], 3}, {[0, 1, 1], 4},
+ {[1, 0, 0], 5}, {[1, 0, 1], 6}, {[1, 1, 0], 7}, {[1, 1, 1], 8}]},
+ {"empty object", <<"{}">>, {}},
+ {"one element simple object",
+ <<"{\"a\": 1}">>,
+ [{[<<"a">>], 1}]},
+ {"two elements simple object",
+ <<"{\"a\": 1, \"b\": 2}">>,
+ [{[<<"a">>], 1}, {[<<"b">>], 2}]},
+ {"one element deep object",
+ <<"{\"a\": {\"b\": {\"c\": 3, \"d\": 4}}}">>,
+ [{[<<"a">>, <<"b">>, <<"c">>], 3},
+ {[<<"a">>, <<"b">>, <<"d">>], 4}]},
+ {"two elements deep object",
+ <<"{\"a\": {\"b\": 2, \"c\": 3}, \"d\": {\"e\": 5, \"f\": 6}}">>,
+ [{[<<"a">>, <<"b">>], 2}, {[<<"a">>, <<"c">>], 3},
+ {[<<"d">>, <<"e">>], 5}, {[<<"d">>, <<"f">>], 6}]},
+ {"three elements mixed depth object",
+ <<"{\"a\": {\"b\": {\"c\": {\"d\": 4, \"e\": \"f\"}},"
+ "\"g\": 7}, \"h\": true, \"i\": {\"j\": null}}">>,
+ [{[<<"a">>, <<"b">>, <<"c">>, <<"d">>], 4},
+ {[<<"a">>, <<"b">>, <<"c">>, <<"e">>], <<"f">>},
+ {[<<"a">>, <<"g">>], 7}, {[<<"h">>], true},
+ {[<<"i">>, <<"j">>], null}]},
+ {"object of lists", <<"{\"a\": [1, 2], \"b\": [3, 4]}">>,
+ [{[<<"a">>, 0], 1}, {[<<"a">>, 1], 2},
+ {[<<"b">>, 0], 3}, {[<<"b">>, 1], 4}]},
+ {"list of objects",
+ <<"[{\"a\": 1, \"b\": 2}, {\"c\": 3, \"d\": 4}]">>,
+ [{[0, <<"a">>], 1}, {[0, <<"b">>], 2},
+ {[1, <<"c">>], 3}, {[1, <<"d">>], 4}]},
+ {"object of lists of objects",
+ <<"{\"a\": [{\"c\": 3, \"d\": 4}, {\"e\": 5, \"f\": 6}],"
+ "\"b\": [{\"g\": 7, \"h\": 8}, {\"i\": 9, \"j\": 10}]}">>,
+ [{[<<"a">>, 0, <<"c">>], 3}, {[<<"a">>, 0, <<"d">>], 4},
+ {[<<"a">>, 1, <<"e">>], 5}, {[<<"a">>, 1, <<"f">>], 6},
+ {[<<"b">>, 0, <<"g">>], 7}, {[<<"b">>, 0, <<"h">>], 8},
+ {[<<"b">>, 1, <<"i">>], 9}, {[<<"b">>, 1, <<"j">>], 10}]},
+ {"one element object with an empty list value",
+ <<"{\"a\": []}">>,
+ [{[<<"a">>], []}]},
+ {"two elements deep object with an empty list values",
+ <<"{\"a\": {\"b\": []}, \"c\": {\"d\": []}}">>,
+ [{[<<"a">>, <<"b">>], []}, {[<<"c">>, <<"d">>], []}]},
+ {"one element object with an empty object value",
+ <<"{\"a\": {}}">>,
+ [{[<<"a">>], {}}]},
+ {"two elements deep object with an empty object values",
+ <<"{\"a\": {\"b\": {}}, \"c\": {\"d\": {}}}">>,
+ [{[<<"a">>, <<"b">>], {}}, {[<<"c">>, <<"d">>], {}}]},
+ {"list with of empty lists", <<"[[], [], []]">>,
+ [{[0], []}, {[1], []}, {[2], []}]},
+ {"list with of empty objects", <<"[{}, {}, {}]">>,
+ [{[0], {}}, {[1], {}}, {[2], {}}]},
+ {"deep list of mixed values", <<"[1, [[], 2], [3, {}]]">>,
+ [{[0], 1}, {[1, 0], []}, {[1, 1], 2}, {[2, 0], 3}, {[2, 1], {}}]}
+ ].