476 lines
15 KiB
Markdown
476 lines
15 KiB
Markdown
# PLJSON_HELPER
|
|
|
|
## Package Specification
|
|
|
|
```sql
|
|
package pljson_helper as
|
|
/* Example:
|
|
set serveroutput on;
|
|
declare
|
|
v_a json;
|
|
v_b json;
|
|
begin
|
|
v_a := json('{a:1, b:{a:null}, e:false}');
|
|
v_b := json('{c:3, e:{}, b:{b:2}}');
|
|
json_helper.merge(v_a, v_b).print(false);
|
|
end;
|
|
--
|
|
{"a":1,"b":{"a":null,"b":2},"e":{},"c":3}
|
|
*/
|
|
-- Recursive merge
|
|
-- Courtesy of Matt Nolan - edited by Jonas Krogsboell
|
|
function merge(p_a_json pljson, p_b_json pljson) return pljson;
|
|
|
|
-- Join two lists
|
|
-- json_helper.join(json_list('[1,2,3]'),json_list('[4,5,6]')) -> [1,2,3,4,5,6]
|
|
function join(p_a_list pljson_list, p_b_list pljson_list) return pljson_list;
|
|
|
|
-- keep only specific keys in json object
|
|
-- json_helper.keep(json('{a:1,b:2,c:3,d:4,e:5,f:6}'),json_list('["a","f","c"]')) -> {"a":1,"f":6,"c":3}
|
|
function keep(p_json pljson, p_keys pljson_list) return pljson;
|
|
|
|
-- remove specific keys in json object
|
|
-- json_helper.remove(json('{a:1,b:2,c:3,d:4,e:5,f:6}'),json_list('["a","f","c"]')) -> {"b":2,"d":4,"e":5}
|
|
function remove(p_json pljson, p_keys pljson_list) return pljson;
|
|
|
|
--equals
|
|
function equals(p_v1 pljson_element, p_v2 pljson_element, exact boolean default true) return boolean;
|
|
function equals(p_v1 pljson_element, p_v2 pljson, exact boolean default true) return boolean;
|
|
function equals(p_v1 pljson_element, p_v2 pljson_list, exact boolean default true) return boolean;
|
|
function equals(p_v1 pljson_element, p_v2 number) return boolean;
|
|
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
|
|
function equals(p_v1 pljson_element, p_v2 binary_double) return boolean;
|
|
function equals(p_v1 pljson_element, p_v2 varchar2) return boolean;
|
|
function equals(p_v1 pljson_element, p_v2 boolean) return boolean;
|
|
function equals(p_v1 pljson_element, p_v2 clob) return boolean;
|
|
function equals(p_v1 pljson, p_v2 pljson, exact boolean default true) return boolean;
|
|
function equals(p_v1 pljson_list, p_v2 pljson_list, exact boolean default true) return boolean;
|
|
|
|
--contains json, json_value
|
|
--contains json_list, json_value
|
|
function contains(p_v1 pljson, p_v2 pljson_element, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson, p_v2 pljson, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson, p_v2 pljson_list, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson, p_v2 number, exact boolean default false) return boolean;
|
|
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
|
|
function contains(p_v1 pljson, p_v2 binary_double, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson, p_v2 varchar2, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson, p_v2 boolean, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson, p_v2 clob, exact boolean default false) return boolean;
|
|
|
|
function contains(p_v1 pljson_list, p_v2 pljson_element, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson_list, p_v2 pljson, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson_list, p_v2 pljson_list, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson_list, p_v2 number, exact boolean default false) return boolean;
|
|
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
|
|
function contains(p_v1 pljson_list, p_v2 binary_double, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson_list, p_v2 varchar2, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson_list, p_v2 boolean, exact boolean default false) return boolean;
|
|
function contains(p_v1 pljson_list, p_v2 clob, exact boolean default false) return boolean;
|
|
|
|
end pljson_helper;```
|
|
|
|
## Package Body
|
|
|
|
```sql
|
|
package body pljson_helper as
|
|
|
|
--recursive merge
|
|
function merge(p_a_json pljson, p_b_json pljson) return pljson as
|
|
l_json pljson;
|
|
l_jv pljson_element;
|
|
l_indx number;
|
|
l_recursive pljson_element;
|
|
begin
|
|
--
|
|
-- Initialize our return object
|
|
--
|
|
l_json := p_a_json;
|
|
|
|
-- loop through p_b_json
|
|
l_indx := p_b_json.json_data.first;
|
|
loop
|
|
exit when l_indx is null;
|
|
l_jv := p_b_json.json_data(l_indx);
|
|
if (l_jv.is_object) then
|
|
--recursive
|
|
l_recursive := l_json.get(l_jv.mapname);
|
|
if (l_recursive is not null and l_recursive.is_object) then
|
|
l_json.put(l_jv.mapname, merge(pljson(l_recursive), pljson(l_jv)));
|
|
else
|
|
l_json.put(l_jv.mapname, l_jv);
|
|
end if;
|
|
else
|
|
l_json.put(l_jv.mapname, l_jv);
|
|
end if;
|
|
|
|
--increment
|
|
l_indx := p_b_json.json_data.next(l_indx);
|
|
end loop;
|
|
|
|
return l_json;
|
|
|
|
end merge;
|
|
|
|
-- join two lists
|
|
function join(p_a_list pljson_list, p_b_list pljson_list) return pljson_list as
|
|
l_json_list pljson_list := p_a_list;
|
|
begin
|
|
for indx in 1 .. p_b_list.count loop
|
|
l_json_list.append(p_b_list.get(indx));
|
|
end loop;
|
|
|
|
return l_json_list;
|
|
|
|
end join;
|
|
|
|
-- keep keys.
|
|
function keep(p_json pljson, p_keys pljson_list) return pljson as
|
|
l_json pljson := pljson();
|
|
mapname varchar2(4000);
|
|
begin
|
|
for i in 1 .. p_keys.count loop
|
|
mapname := p_keys.get(i).get_string();
|
|
if (p_json.exist(mapname)) then
|
|
l_json.put(mapname, p_json.get(mapname));
|
|
end if;
|
|
end loop;
|
|
|
|
return l_json;
|
|
end keep;
|
|
|
|
-- drop keys.
|
|
function remove(p_json pljson, p_keys pljson_list) return pljson as
|
|
l_json pljson := p_json;
|
|
begin
|
|
for i in 1 .. p_keys.count loop
|
|
l_json.remove(p_keys.get(i).get_string());
|
|
end loop;
|
|
|
|
return l_json;
|
|
end remove;
|
|
|
|
--equals functions
|
|
|
|
function equals(p_v1 pljson_element, p_v2 number) return boolean as
|
|
begin
|
|
if (p_v2 is null) then
|
|
return p_v1.is_null;
|
|
end if;
|
|
|
|
if (not p_v1.is_number) then
|
|
return false;
|
|
end if;
|
|
|
|
return p_v2 = p_v1.get_number();
|
|
end;
|
|
|
|
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
|
|
function equals(p_v1 pljson_element, p_v2 binary_double) return boolean as
|
|
begin
|
|
if (p_v2 is null) then
|
|
return p_v1.is_null;
|
|
end if;
|
|
|
|
if (not p_v1.is_number) then
|
|
return false;
|
|
end if;
|
|
|
|
return p_v2 = p_v1.get_double();
|
|
end;
|
|
|
|
function equals(p_v1 pljson_element, p_v2 boolean) return boolean as
|
|
begin
|
|
if (p_v2 is null) then
|
|
return p_v1.is_null;
|
|
end if;
|
|
|
|
if (not p_v1.is_bool) then
|
|
return false;
|
|
end if;
|
|
|
|
return p_v2 = p_v1.get_bool();
|
|
end;
|
|
|
|
function equals(p_v1 pljson_element, p_v2 varchar2) return boolean as
|
|
begin
|
|
if (p_v2 is null) then
|
|
return (p_v1.is_null or p_v1.get_string() is null);
|
|
end if;
|
|
|
|
if (not p_v1.is_string) then
|
|
return false;
|
|
end if;
|
|
|
|
return p_v2 = p_v1.get_string();
|
|
end;
|
|
|
|
function equals(p_v1 pljson_element, p_v2 clob) return boolean as
|
|
my_clob clob;
|
|
res boolean;
|
|
begin
|
|
if (p_v2 is null) then
|
|
return p_v1.is_null;
|
|
end if;
|
|
|
|
if (not p_v1.is_string) then
|
|
return false;
|
|
end if;
|
|
|
|
/*
|
|
my_clob := empty_clob();
|
|
dbms_lob.createtemporary(my_clob, true);
|
|
p_v1.get_string(my_clob);
|
|
*/
|
|
my_clob := p_v1.get_clob();
|
|
res := dbms_lob.compare(p_v2, my_clob) = 0;
|
|
/*dbms_lob.freetemporary(my_clob);*/
|
|
return res;
|
|
end;
|
|
|
|
function equals(p_v1 pljson_element, p_v2 pljson_element, exact boolean) return boolean as
|
|
begin
|
|
if (p_v2 is null or p_v2.is_null) then
|
|
return (p_v1 is null or p_v1.is_null);
|
|
end if;
|
|
|
|
if (p_v2.is_number) then return equals(p_v1, p_v2.get_number); end if;
|
|
if (p_v2.is_bool) then return equals(p_v1, p_v2.get_bool); end if;
|
|
if (p_v2.is_object) then return equals(p_v1, pljson(p_v2), exact); end if;
|
|
if (p_v2.is_array) then return equals(p_v1, pljson_list(p_v2), exact); end if;
|
|
if (p_v2.is_string) then
|
|
if (treat(p_v2 as pljson_string).extended_str is null) then
|
|
return equals(p_v1, p_v2.get_string);
|
|
else
|
|
declare
|
|
my_clob clob; res boolean;
|
|
begin
|
|
/*
|
|
my_clob := empty_clob();
|
|
dbms_lob.createtemporary(my_clob, true);
|
|
p_v2.get_string(my_clob);
|
|
*/
|
|
my_clob := p_v2.get_clob();
|
|
res := equals(p_v1, my_clob);
|
|
/*dbms_lob.freetemporary(my_clob);*/
|
|
return res;
|
|
end;
|
|
end if;
|
|
end if;
|
|
|
|
return false; --should never happen
|
|
end;
|
|
|
|
function equals(p_v1 pljson_element, p_v2 pljson_list, exact boolean) return boolean as
|
|
cmp pljson_list;
|
|
res boolean := true;
|
|
begin
|
|
-- p_v1.print(false);
|
|
-- p_v2.print(false);
|
|
-- dbms_output.put_line('labc1'||case when exact then 'X' else 'U' end);
|
|
|
|
if (p_v2 is null) then
|
|
return p_v1.is_null;
|
|
end if;
|
|
|
|
if (not p_v1.is_array) then
|
|
return false;
|
|
end if;
|
|
|
|
-- dbms_output.put_line('labc2'||case when exact then 'X' else 'U' end);
|
|
|
|
cmp := pljson_list(p_v1);
|
|
if (cmp.count != p_v2.count and exact) then return false; end if;
|
|
|
|
-- dbms_output.put_line('labc3'||case when exact then 'X' else 'U' end);
|
|
|
|
if (exact) then
|
|
for i in 1 .. cmp.count loop
|
|
res := equals(cmp.get(i), p_v2.get(i), exact);
|
|
if (not res) then return res; end if;
|
|
end loop;
|
|
else
|
|
-- dbms_output.put_line('labc4'||case when exact then 'X' else 'U' end);
|
|
if (p_v2.count > cmp.count) then return false; end if;
|
|
-- dbms_output.put_line('labc5'||case when exact then 'X' else 'U' end);
|
|
|
|
--match sublist here!
|
|
for x in 0 .. (cmp.count-p_v2.count) loop
|
|
-- dbms_output.put_line('labc7'||x);
|
|
|
|
for i in 1 .. p_v2.count loop
|
|
res := equals(cmp.get(x+i), p_v2.get(i), exact);
|
|
if (not res) then
|
|
goto next_index;
|
|
end if;
|
|
end loop;
|
|
return true;
|
|
|
|
<<next_index>>
|
|
null;
|
|
end loop;
|
|
|
|
-- dbms_output.put_line('labc7'||case when exact then 'X' else 'U' end);
|
|
|
|
return false; --no match
|
|
|
|
end if;
|
|
|
|
return res;
|
|
end;
|
|
|
|
function equals(p_v1 pljson_element, p_v2 pljson, exact boolean) return boolean as
|
|
cmp pljson;
|
|
res boolean := true;
|
|
begin
|
|
-- p_v1.print(false);
|
|
-- p_v2.print(false);
|
|
-- dbms_output.put_line('abc1');
|
|
|
|
if (p_v2 is null) then
|
|
return p_v1.is_null;
|
|
end if;
|
|
|
|
if (not p_v1.is_object) then
|
|
return false;
|
|
end if;
|
|
|
|
cmp := pljson(p_v1);
|
|
|
|
-- dbms_output.put_line('abc2');
|
|
|
|
if (cmp.count != p_v2.count and exact) then return false; end if;
|
|
|
|
-- dbms_output.put_line('abc3');
|
|
declare
|
|
k1 pljson_list := p_v2.get_keys();
|
|
key_index number;
|
|
begin
|
|
for i in 1 .. k1.count loop
|
|
key_index := cmp.index_of(k1.get(i).get_string());
|
|
if (key_index = -1) then return false; end if;
|
|
if (exact) then
|
|
if (not equals(p_v2.get(i), cmp.get(key_index), true)) then return false; end if;
|
|
else
|
|
--non exact
|
|
declare
|
|
v1 pljson_element := cmp.get(key_index);
|
|
v2 pljson_element := p_v2.get(i);
|
|
begin
|
|
-- dbms_output.put_line('abc3 1/2');
|
|
-- v1.print(false);
|
|
-- v2.print(false);
|
|
|
|
if (v1.is_object and v2.is_object) then
|
|
if (not equals(v1, v2, false)) then return false; end if;
|
|
elsif (v1.is_array and v2.is_array) then
|
|
if (not equals(v1, v2, false)) then return false; end if;
|
|
else
|
|
if (not equals(v1, v2, true)) then return false; end if;
|
|
end if;
|
|
end;
|
|
|
|
end if;
|
|
end loop;
|
|
end;
|
|
|
|
-- dbms_output.put_line('abc4');
|
|
|
|
return true;
|
|
end;
|
|
|
|
function equals(p_v1 pljson, p_v2 pljson, exact boolean) return boolean as
|
|
begin
|
|
return equals(p_v1, p_v2, exact);
|
|
end;
|
|
|
|
function equals(p_v1 pljson_list, p_v2 pljson_list, exact boolean) return boolean as
|
|
begin
|
|
return equals(p_v1, p_v2, exact);
|
|
end;
|
|
|
|
--contain
|
|
function contains(p_v1 pljson, p_v2 pljson_element, exact boolean) return boolean as
|
|
v_values pljson_list;
|
|
begin
|
|
if (equals(p_v1, p_v2, exact)) then return true; end if;
|
|
|
|
v_values := p_v1.get_values();
|
|
|
|
for i in 1 .. v_values.count loop
|
|
declare
|
|
v_val pljson_element := v_values.get(i);
|
|
begin
|
|
if (v_val.is_object) then
|
|
if (contains(pljson(v_val), p_v2, exact)) then return true; end if;
|
|
end if;
|
|
if (v_val.is_array) then
|
|
if (contains(pljson_list(v_val), p_v2, exact)) then return true; end if;
|
|
end if;
|
|
|
|
if (equals(v_val, p_v2, exact)) then return true; end if;
|
|
end;
|
|
|
|
end loop;
|
|
|
|
return false;
|
|
end;
|
|
|
|
function contains(p_v1 pljson_list, p_v2 pljson_element, exact boolean) return boolean as
|
|
begin
|
|
if (equals(p_v1, p_v2, exact)) then return true; end if;
|
|
|
|
for i in 1 .. p_v1.count loop
|
|
declare
|
|
v_val pljson_element := p_v1.get(i);
|
|
begin
|
|
if (v_val.is_object) then
|
|
if (contains(pljson(v_val), p_v2, exact)) then return true; end if;
|
|
end if;
|
|
if (v_val.is_array) then
|
|
if (contains(pljson_list(v_val), p_v2, exact)) then return true; end if;
|
|
end if;
|
|
|
|
if (equals(v_val, p_v2, exact)) then return true; end if;
|
|
end;
|
|
|
|
end loop;
|
|
|
|
return false;
|
|
end;
|
|
|
|
function contains(p_v1 pljson, p_v2 pljson, exact boolean ) return boolean as
|
|
begin return contains(p_v1, p_v2, exact); end;
|
|
function contains(p_v1 pljson, p_v2 pljson_list, exact boolean ) return boolean as
|
|
begin return contains(p_v1, p_v2, exact); end;
|
|
function contains(p_v1 pljson, p_v2 number, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_number(p_v2), exact); end;
|
|
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
|
|
function contains(p_v1 pljson, p_v2 binary_double, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_number(p_v2), exact); end;
|
|
function contains(p_v1 pljson, p_v2 varchar2, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_string(p_v2), exact); end;
|
|
function contains(p_v1 pljson, p_v2 boolean, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_bool(p_v2), exact); end;
|
|
function contains(p_v1 pljson, p_v2 clob, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_string(p_v2), exact); end;
|
|
|
|
function contains(p_v1 pljson_list, p_v2 pljson, exact boolean ) return boolean as begin
|
|
return contains(p_v1, p_v2, exact); end;
|
|
function contains(p_v1 pljson_list, p_v2 pljson_list, exact boolean ) return boolean as begin
|
|
return contains(p_v1, p_v2, exact); end;
|
|
function contains(p_v1 pljson_list, p_v2 number, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_number(p_v2), exact); end;
|
|
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
|
|
function contains(p_v1 pljson_list, p_v2 binary_double, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_number(p_v2), exact); end;
|
|
function contains(p_v1 pljson_list, p_v2 varchar2, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_string(p_v2), exact); end;
|
|
function contains(p_v1 pljson_list, p_v2 boolean, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_bool(p_v2), exact); end;
|
|
function contains(p_v1 pljson_list, p_v2 clob, exact boolean ) return boolean as begin
|
|
return contains(p_v1, pljson_string(p_v2), exact); end;
|
|
|
|
|
|
end pljson_helper;```
|