# 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; <> 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;```