The limitation of working with JSONs is that they can come in a variety of structures with multiple levels of nesting and a combination of JSON objects, JSON arrays and key-value pairs.
This makes parsing and creating JSONs with such varied structures a challenge.
The JSON creation can be simplified to some extent by creating a configuration table, storing the JSON data in a particular order and format by which it becomes easier to create the JSON.
Below is a sample JSON I have conjured which has multiple levels of nesting of arrays, objects and key-value pairs. This I felt would be as good as it gets. Considering the wide varieity of JSON types and structures, though it may not be a completely generic JSON creator, it may work for a majority of cases. And with a bit of tweaking of the code and handling the boundary conditions it should be a workable customized solution for any JSON.
Sample JSON
{
"JsonList": {
"JsonObj": {
"objtest1": {
"test1key1": "test1value1",
"test1key2": "test1value2"
},
"ID": "1",
"ObjName": "jsonParser",
"objtest": {
"key1": "value1",
"objtest2": {
"test2key1": "test2value1",
"test2key2": "test2value2"
},
"key2": "value2"
},
"nestedarray": [
"json1",
"xml1"
]
},
"keyvaluetest": "markup",
"projects": [
"json",
"xml"
]
},
"employees": [
{
"name": "Shelley,Higgins",
"job": "Accounting Manager"
},
{
"name": "William,Gietz",
"job": "Public Accountant"
}
]
}
With this JSON we now create the JSON table which will store the information for the levels of nesting, the order of each element, the type of each element, its relation with the parent key. Once we have tableized this information, we can build a fairly simple plsql algorithm to create the JSON.CREATE TABLE JSON_MATRIX_CONFIG
(
JSON_LEVEL NUMBER,
JSON_KEY VARCHAR2(50 BYTE),
JSON_TYPE VARCHAR2(50 BYTE),
JSON_VALUE VARCHAR2(4000 BYTE),
PARENT_KEY VARCHAR2(100 BYTE),
PARENT_JSON_TYPE VARCHAR2(50 BYTE),
LEVEL_SEQ NUMBER,
JSON_SRL_NO NUMBER
);
Below is the description and usage of each column in the table. JSON_LEVEL | JSON_KEY | JSON_TYPE | JSON_VALUE | PARENT_KEY | PARENT_JSON_TYPE | LEVEL_SEQ | JSON_SRL_NO |
---|---|---|---|---|---|---|---|
1 | JsonList | object | 1 | 1 | |||
2 | JsonObj | object | JsonList | object | 1 | 2 | |
3 | objtest1 | object | JsonObj | object | 1 | 3 | |
4 | test1key1 | string | test1value1 | objtest1 | object | 1 | 4 |
4 | test1key2 | string | test1value2 | objtest1 | object | 2 | 5 |
3 | ID | string | 1 | JsonObj | object | 2 | 6 |
3 | ObjName | string | jsonParser | JsonObj | object | 3 | 7 |
3 | objtest | object | JsonObj | object | 4 | 8 | |
4 | key1 | string | value1 | objtest | object | 1 | 9 |
4 | objtest2 | object | objtest | object | 2 | 10 | |
5 | test2key1 | string | test2value1 | objtest2 | object | 1 | 11 |
5 | test2key2 | string | test2value2 | objtest2 | object | 2 | 12 |
4 | key2 | string | value2 | objtest | object | 3 | 13 |
3 | nestedarray | array | JsonObj | object | 5 | 14 | |
4 | string | json1 | nestedarray | array | 1 | 15 | |
4 | string | xml1 | nestedarray | array | 2 | 16 | |
2 | keyvaluetest | string | markup | JsonList | object | 2 | 17 |
2 | projects | array | JsonList | object | 3 | 18 | |
3 | string | json | projects | array | 1 | 19 | |
3 | string | xml | projects | array | 2 | 20 | |
1 | employees | array | 2 | 21 | |||
2 | name | string | Shelley,Higgins | employees | array | 1 | 22 |
2 | job | string | Accounting Manager | employees | array | 1 | 23 |
2 | name | string | William,Gietz | employees | array | 2 | 24 |
2 | job | string | Public Accountant | employees | array | 2 | 25 |
CREATE OR REPLACE PACKAGE json_traversal_generate
AS
PROCEDURE gnrt_json_from_base;
END;
/
The package body
CREATE OR REPLACE PACKAGE BODY json_traversal_generate
AS
PROCEDURE gnrt_json_from_base
IS
--Cursor to select all level 1 JSONs
CURSOR C0
IS
SELECT JSON_KEY, JSON_SRL_NO min_srl_no,
(SELECT NVL(MIN(JSON_SRL_NO),999) FROM json_matrix_config_base b WHERE PARENT_KEY IS NULL AND b.JSON_SRL_NO > a.JSON_SRL_NO) max_srl_no,
JSON_VALUE, JSON_TYPE
FROM json_matrix_config_base a
WHERE PARENT_KEY IS NULL
ORDER BY LEVEL_SEQ;
--Cursor to select parent keys for these JSONs
CURSOR C1 (min_srl_no NUMBER, max_srl_no NUMBER)
IS
SELECT DISTINCT JSON_LEVEL, PARENT_KEY, JSON_TYPE, JSON_SRL_NO
FROM json_matrix_config_base
WHERE JSON_SRL_NO >= min_srl_no AND JSON_SRL_NO < max_srl_no
ORDER BY JSON_LEVEL DESC, JSON_SRL_NO;
--Cursor to select the key-value pairs in each parent JSON
CURSOR C2(p_parent_key VARCHAR2)
IS
SELECT PARENT_KEY, PARENT_JSON_TYPE, JSON_KEY, JSON_VALUE, JSON_TYPE, LEVEL_SEQ
FROM json_matrix_config_base
WHERE PARENT_KEY = p_parent_key
ORDER BY LEVEL_SEQ;
jo_temp JSON_OBJECT_T := JSON_OBJECT_T();
jo_temp_1 JSON_OBJECT_T := JSON_OBJECT_T();
jo_temp_final JSON_OBJECT_T := JSON_OBJECT_T();
l_arr JSON_ARRAY_T := JSON_ARRAY_T();
l_arr_1 JSON_ARRAY_T := JSON_ARRAY_T();
l_arr_final JSON_ARRAY_T := JSON_ARRAY_T();
tmp_obj_str CLOB; --VARCHAR2(4000);
tmp_arr_str VARCHAR2(4000);
tmp_json_key VARCHAR2(100);
prev_level_seq NUMBER := 0;
key_value BOOLEAN := FALSE;
BEGIN
FOR rec_srl IN C0 -- Fetch each LEVEL 1 JSON in the structure
LOOP --{
FOR pkey IN C1(rec_srl.min_srl_no, rec_srl.max_srl_no)
LOOP -- get the parent keys for each JSON
jo_temp := JSON_OBJECT_T();
jo_temp_1 := JSON_OBJECT_T();
l_arr := JSON_ARRAY_T();
prev_level_seq := 0;
key_value := FALSE;
FOR rec IN C2(pkey.PARENT_KEY)
LOOP --get the individual elements for each parent JSON
IF rec.JSON_TYPE = 'object' -- If it is an object add each element to that object
THEN
jo_temp_1 := JSON_OBJECT_T(rec.JSON_VALUE);
jo_temp.put(rec.JSON_KEY,jo_temp_1);
ELSIF rec.JSON_TYPE = 'array' -- If it is an array add each element to that array
THEN
l_arr_1 := JSON_ARRAY_T(rec.JSON_VALUE);
jo_temp.put(rec.JSON_KEY,l_arr_1);
ELSIF rec.JSON_TYPE = 'string'
THEN
IF rec.JSON_KEY IS NULL
THEN
l_arr.append(rec.JSON_VALUE); -- it is a simple JSON array of strings
ELSE
key_value := TRUE;
IF prev_level_seq <> 0 AND prev_level_seq <> rec.LEVEL_SEQ AND rec.PARENT_JSON_TYPE = 'array'-- it is an object array. append each temp json object to the json array
THEN
-- dbms_output.put_line ('prev_level_seq ' || prev_level_seq);
-- dbms_output.put_line ('jo_temp ' || jo_temp.to_string);
l_arr.append(jo_temp);
jo_temp := JSON_OBJECT_T();
END IF;
jo_temp.put(rec.JSON_KEY, rec.JSON_VALUE); -- add the key value pair of the json object to a temp object.
-- dbms_output.put_line('jo_temp in loop' || jo_temp.to_string);
END IF;
END IF;
prev_level_seq := rec.LEVEL_SEQ;
END LOOP;
IF NOT l_arr.IS_NULL
THEN
IF key_value = TRUE
THEN
l_arr.append(jo_temp); --add the last json object to the array.
END IF;
tmp_arr_str := l_arr.to_string;
UPDATE json_matrix_config_base a --update the intermediate json array in JSON_VALUE column for that array key.
SET JSON_VALUE = tmp_arr_str
WHERE JSON_KEY = pkey.PARENT_KEY
AND JSON_TYPE = 'array'
AND LEVEL_SEQ = (
SELECT MAX(LEVEL_SEQ)
FROM json_matrix_config_base b
WHERE JSON_KEY = pkey.PARENT_KEY
AND JSON_TYPE = 'array')
AND JSON_LEVEL = pkey.JSON_LEVEL - 1;
END IF;
-- dbms_output.put_line('temp json ' || jo_temp.to_string);
tmp_obj_str := jo_temp.to_string;
tmp_obj_str := TRIM (BOTH '"' FROM (tmp_obj_str));
UPDATE json_matrix_config_base a --update the intermediate json object in JSON_VALUE column for that array key.
SET JSON_VALUE = tmp_obj_str
WHERE JSON_KEY = pkey.PARENT_KEY
AND JSON_TYPE = 'object'
AND LEVEL_SEQ = (
SELECT MAX(LEVEL_SEQ)
FROM json_matrix_config_base b
WHERE JSON_KEY = pkey.PARENT_KEY
AND JSON_TYPE = 'object')
AND JSON_LEVEL = pkey.JSON_LEVEL - 1;
END LOOP;
--We then finally add each JSON object to the final JSON.
IF rec_srl.JSON_TYPE = 'object'
THEN
-- dbms_output.put_line('final object json str ' || tmp_obj_str);
SELECT JSON_VALUE
INTO tmp_obj_str
FROM json_matrix_config_base
WHERE PARENT_KEY IS NULL
AND JSON_TYPE = rec_srl.JSON_TYPE
AND JSON_KEY = rec_srl.JSON_KEY;
tmp_obj_str := TRIM (BOTH '"' FROM (tmp_obj_str));
jo_temp_1 := JSON_OBJECT_T(tmp_obj_str);
jo_temp_final.put(rec_srl.JSON_KEY,jo_temp_1);
END IF;
--We then finally add each JSON array to the final JSON.
IF rec_srl.JSON_TYPE = 'array'
THEN
SELECT JSON_VALUE
INTO tmp_arr_str
FROM json_matrix_config_base
WHERE PARENT_KEY IS NULL
AND JSON_TYPE = rec_srl.JSON_TYPE
AND JSON_KEY = rec_srl.JSON_KEY;
tmp_arr_str := rec_srl.JSON_VALUE;
tmp_arr_str := TRIM (BOTH '"' FROM (tmp_arr_str));
l_arr_1 := JSON_ARRAY_T(tmp_arr_str);
jo_temp_final.put(rec_srl.JSON_KEY,l_arr_1);
END IF;
-- dbms_output.put_line('final final json ' || jo_temp_final.to_string);
END LOOP; --} end C0
--This will display the final JSON which is created
dbms_output.put_line('final json ' || jo_temp_final.to_string);
END;
END json_traversal_generate;
/
BEGIN
json_traversal_generate.gnrt_json_from_base;
END;
{
"JsonList": {
"JsonObj": {
"objtest1": {
"test1key1": "test1value1",
"test1key2": "test1value2"
},
"ID": "1",
"ObjName": "jsonParser",
"objtest": {
"key1": "value1",
"objtest2": {
"test2key1": "test2value1",
"test2key2": "test2value2"
},
"key2": "value2"
},
"nestedarray": [
"json1",
"xml1"
]
},
"keyvaluetest": "markup",
"projects": [
"json",
"xml"
]
},
"employees": [
{
"name": "Shelley,Higgins",
"job": "Accounting Manager"
},
{
"name": "William,Gietz",
"job": "Public Accountant"
}
]
}
JSON_LEVEL | JSON_KEY | JSON_TYPE | JSON_VALUE | PARENT_KEY | PARENT_JSON_TYPE | LEVEL_SEQ | JSON_SRL_NO |
---|---|---|---|---|---|---|---|
1 | JsonList | object | {"JsonObj":{"objtest1":{"test1key1":"test1value1","test1key2":"test1value2"},"ID":"1","ObjName":"jsonParser","objtest":{"key1":"value1","objtest2":{"test2key1":"test2value1","test2key2":"test2value2"},"key2":"value2"},"nestedarray":["json1","xml1"]},"keyvaluetest":"markup","projects":["json","xml"]} | 1 | 1 | ||
2 | JsonObj | object | {"objtest1":{"test1key1":"test1value1","test1key2":"test1value2"},"ID":"1","ObjName":"jsonParser","objtest":{"key1":"value1","objtest2":{"test2key1":"test2value1","test2key2":"test2value2"},"key2":"value2"},"nestedarray":["json1","xml1"]} | JsonList | object | 1 | 2 |
3 | objtest1 | object | {"test1key1":"test1value1","test1key2":"test1value2"} | JsonObj | object | 1 | 3 |
4 | test1key1 | string | test1value1 | objtest1 | object | 1 | 4 |
4 | test1key2 | string | test1value2 | objtest1 | object | 2 | 5 |
3 | ID | string | 1 | JsonObj | object | 2 | 6 |
3 | ObjName | string | jsonParser | JsonObj | object | 3 | 7 |
3 | objtest | object | {"key1":"value1","objtest2":{"test2key1":"test2value1","test2key2":"test2value2"},"key2":"value2"} | JsonObj | object | 4 | 8 |
4 | key1 | string | value1 | objtest | object | 1 | 9 |
4 | objtest2 | object | {"test2key1":"test2value1","test2key2":"test2value2"} | objtest | object | 2 | 10 |
5 | test2key1 | string | test2value1 | objtest2 | object | 1 | 11 |
5 | test2key2 | string | test2value2 | objtest2 | object | 2 | 12 |
4 | key2 | string | value2 | objtest | object | 3 | 13 |
3 | nestedarray | array | ["json1","xml1"] | JsonObj | object | 5 | 14 |
4 | string | json1 | nestedarray | array | 1 | 15 | |
4 | string | xml1 | nestedarray | array | 2 | 16 | |
2 | keyvaluetest | string | markup | JsonList | object | 2 | 17 |
2 | projects | array | ["json","xml"] | JsonList | object | 3 | 18 |
3 | string | json | projects | array | 1 | 19 | |
3 | string | xml | projects | array | 2 | 20 | |
1 | employees | array | [{"name":"Shelley,Higgins","job":"Accounting Manager"},{"name":"William,Gietz","job":"Public Accountant"}] | 2 | 21 | ||
2 | name | string | Shelley,Higgins | employees | array | 1 | 22 |
2 | job | string | Accounting Manager | employees | array | 1 | 23 |
2 | name | string | William,Gietz | employees | array | 2 | 24 |
2 | job | string | Public Accountant | employees | array | 2 | 25 |
No comments:
Post a Comment