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], {}}]}
+    ].