分层 JSON 到 oracle table
Hierarchal JSON to oracle table
我有一个 JSON 对象存储在代表层次结构的 CLOB 中:
{
"Version": 1,
"nodes": [{
"id": 0,
"Fields": ["ABC"],
"nodes": [{
"id": 1,
"Fields": ["DEF"],
"nodes": [{
"id": 2,
"Fields": ["GHI", "HIG"],
"nodes": []
}
]
}, {
"id": 3,
"Fields": ["XYZ", "YZX"],
"nodes": [{
"id": 4,
"Fields": ["UVW"],
"nodes": [{
"id": 5,
"Fields": ["RST"],
"nodes": []
}
]
}
]
}
]
}
]
}
我需要将其表示为 table:
Ver id Field Parent
1 0 ABC null
1 1 DEF 0
1 2 GHI|HIG 1
1 3 XYZ|YZX 0
1 4 UVW 3
1 5 RST 4
我试过使用 PL/JSON。虽然我能够找到单个数据点,但我遇到了分层性质的困难。
APEX(和 12c)不是一个选项。
有人可以帮忙吗?
我用 pl\sql 编写 jSON 解析器。能否请您查看并取走您需要的。
declare
type t_str_rec is table of varchar2(4000);
type t_str_str is table of t_str_rec;
l_tab t_str_str := t_str_str();
l_count number := 0;
procedure get_number (p_value clob,
p_offset out number,
p_number out varchar2) is
l_cur_var varchar2(1 char);
begin
p_offset := 1;
l_cur_var := dbms_lob.substr(p_value,1,1);
while l_cur_var IN ('0','1','2','3','4','5','6','7','8','9') loop
l_cur_var := dbms_lob.substr(p_value,1,p_offset);
p_offset := p_offset + 1;
end loop;
p_number := dbms_lob.substr(p_value,p_offset-2,1);
p_offset := p_offset-2;
-- dbms_output.put_line('get_number:'||p_offset);
end;
procedure get_q_string (p_value clob,
p_offset out number,
p_string out varchar2) is
l_cur_pos number;
l_cur_var varchar2(1 char);
l_count_quotes number := 0;
l_is_end number := 0;
begin
p_offset := 1;
while l_is_end = 0 loop
p_offset := dbms_lob.instr(p_value,'"',p_offset + 1);
l_cur_var := dbms_lob.substr(p_value,1,p_offset-1);
while l_cur_var = '\' loop
l_count_quotes := l_count_quotes +1;
l_cur_pos := l_cur_pos-1;
l_cur_var := dbms_lob.substr(p_value,1,l_cur_pos-1);
end loop;
if(mod(l_count_quotes,2) = 0) then
l_is_end := 1;
end if;
end loop;
p_string := dbms_lob.substr(p_value,p_offset-2,2);
-- dbms_output.put_line('get_q_string:'||p_offset|| ':'||length(p_string) );
end;
procedure get_one_bracket(p_value clob,
p_bracket varchar2 := '[]',
p_offset out number) is
l_left varchar2(1 char) := substr(p_bracket,1,1);
l_right varchar2(1 char) := substr(p_bracket,2,1);
l_cur_pos number;
begin
l_cur_pos := 1;
p_offset := dbms_lob.instr(p_value,l_right);
l_cur_pos := dbms_lob.instr(p_value,l_left,l_cur_pos+1);
while l_cur_pos > 0 and l_cur_pos < p_offset loop
l_cur_pos := dbms_lob.instr(p_value,l_left,l_cur_pos+1);
p_offset := dbms_lob.instr(p_value,l_right,p_offset+1);
if p_offset < 1 then
p_offset := dbms_lob.getlength(p_value);
exit;
end if;
end loop;
-- p_offset := p_offset-1;
-- dbms_output.put_line('get_one_bracket:'|| p_bracket||':'||p_offset );
end;
procedure parse_json (p_clob clob
, p_parent number) is
l_pos number := 1;
l_offset number := 1;
l_length number := dbms_lob.getlength(p_clob);
l_cur_char varchar2(1 char);
l_rec t_str_rec := t_str_rec();
l_parent number := p_parent;
begin
while(l_pos < l_length) loop
l_cur_char := dbms_lob.substr(p_clob, 1,l_pos);
if l_cur_char = '"' then
l_rec.extend();
get_q_string(dbms_lob.substr(p_clob, l_length-l_pos, l_pos),
l_offset,
l_rec(l_rec.last));
elsif l_cur_char = ':' then null; -- it's just a separator
elsif l_cur_char IN ('0','1','2','3','4','5','6','7','8','9') then
l_rec.extend();
get_number (dbms_lob.substr(p_clob, l_length-l_pos, l_pos),
l_offset,
l_rec(l_rec.last));
elsif l_cur_char = '{' then
l_rec.extend();
l_rec(l_rec.last) := '{}';
get_one_bracket(dbms_lob.substr(p_clob, l_length-l_pos+1, l_pos),
'{}',
l_offset);
declare
l_id number := l_count;
begin
parse_json (dbms_lob.substr(p_clob, l_offset, l_pos+1),
l_id);
end;
elsif l_cur_char = '[' then
l_rec.extend();
l_rec(l_rec.last) := '[]';
get_one_bracket(dbms_lob.substr(p_clob, l_length-l_pos+1, l_pos),
'[]',
l_offset);
l_rec.extend();
l_count := l_count + 1;
l_rec(l_rec.last) := l_count;
l_rec.extend();
l_rec(l_rec.last) := l_parent;
l_tab.extend;
l_tab(l_tab.last) := l_rec;
l_rec := t_str_rec();
declare
l_id number := l_count;
begin
parse_json (dbms_lob.substr(p_clob, l_offset, l_pos+1),
l_id);
end;
elsif l_cur_char = ',' then
IF dbms_lob.substr(p_clob, 1,l_pos-1) NOT IN (']','}',',') then
l_rec.extend();
l_count := l_count + 1;
l_rec(l_rec.last) := l_count;
l_rec.extend();
l_rec(l_rec.last) := l_parent;
l_tab.extend;
l_tab(l_tab.last) := l_rec;
l_rec := t_str_rec();
end if;
end if;
l_pos := l_pos+l_offset;
l_offset := 1;
end loop;
l_rec.extend();
l_count := l_count + 1;
l_rec(l_rec.last) := l_count;
l_rec.extend();
l_rec(l_rec.last) := l_parent;
l_tab.extend;
l_tab(l_tab.last) := l_rec;
l_rec := t_str_rec();
end;
begin
parse_json(
'{
"Version": 1,
"nodes": [{
"id": 0,
"Fields": ["ABC"],
"nodes": [{
"id": 1,
"Fields": ["DEF"],
"nodes": [{
"id": 2,
"Fields": ["GHI", "HIG"],
"nodes": []
}
]
}, {
"id": 3,
"Fields": ["XYZ", "YZX"],
"nodes": [{
"id": 4,
"Fields": ["UVW"],
"nodes": [{
"id": 5,
"Fields": ["RST"],
"nodes": []
}
]
}
]
}
]
}
]
}',l_count);
declare
v_vers varchar2(10);
v_id varchar2(10) ;
v_field varchar2(4000);
v_del varchar2(1);
v_is_field number;
v_parent varchar2(10);
type t_num_decode is table of varchar2(10) index by varchar2(10);
l_num_decode t_num_decode;
begin
for ba in l_tab.first .. l_tab.last loop
if v_vers is null and l_tab(ba)(1) = 'Version' then
v_vers := l_tab(ba)(2);
end if;
if l_tab(ba)(1) = 'id' then
v_id := l_tab(ba)(2);
elsif l_tab(ba)(1) = 'Fields' then
v_is_field := 1;
v_field := '';
elsif l_tab(ba)(1) = 'nodes' and v_id is not null then
if l_num_decode.exists(l_tab(ba)(4)) then
v_parent := l_num_decode(l_tab(ba)(4));
end if;
dbms_output.put_line(v_vers||','||v_id||','||v_field||','||nvl(v_parent,'null'));
l_num_decode(l_tab(ba)(3)) := v_id;
v_is_field := 0;
v_del := '';
elsif v_is_field = 1 then
v_field := v_field||v_del||l_tab(ba)(1);
v_del := '|';
end if;
end loop;
end;
end;
/
我有一个 JSON 对象存储在代表层次结构的 CLOB 中:
{
"Version": 1,
"nodes": [{
"id": 0,
"Fields": ["ABC"],
"nodes": [{
"id": 1,
"Fields": ["DEF"],
"nodes": [{
"id": 2,
"Fields": ["GHI", "HIG"],
"nodes": []
}
]
}, {
"id": 3,
"Fields": ["XYZ", "YZX"],
"nodes": [{
"id": 4,
"Fields": ["UVW"],
"nodes": [{
"id": 5,
"Fields": ["RST"],
"nodes": []
}
]
}
]
}
]
}
]
}
我需要将其表示为 table:
Ver id Field Parent
1 0 ABC null
1 1 DEF 0
1 2 GHI|HIG 1
1 3 XYZ|YZX 0
1 4 UVW 3
1 5 RST 4
我试过使用 PL/JSON。虽然我能够找到单个数据点,但我遇到了分层性质的困难。
APEX(和 12c)不是一个选项。
有人可以帮忙吗?
我用 pl\sql 编写 jSON 解析器。能否请您查看并取走您需要的。
declare
type t_str_rec is table of varchar2(4000);
type t_str_str is table of t_str_rec;
l_tab t_str_str := t_str_str();
l_count number := 0;
procedure get_number (p_value clob,
p_offset out number,
p_number out varchar2) is
l_cur_var varchar2(1 char);
begin
p_offset := 1;
l_cur_var := dbms_lob.substr(p_value,1,1);
while l_cur_var IN ('0','1','2','3','4','5','6','7','8','9') loop
l_cur_var := dbms_lob.substr(p_value,1,p_offset);
p_offset := p_offset + 1;
end loop;
p_number := dbms_lob.substr(p_value,p_offset-2,1);
p_offset := p_offset-2;
-- dbms_output.put_line('get_number:'||p_offset);
end;
procedure get_q_string (p_value clob,
p_offset out number,
p_string out varchar2) is
l_cur_pos number;
l_cur_var varchar2(1 char);
l_count_quotes number := 0;
l_is_end number := 0;
begin
p_offset := 1;
while l_is_end = 0 loop
p_offset := dbms_lob.instr(p_value,'"',p_offset + 1);
l_cur_var := dbms_lob.substr(p_value,1,p_offset-1);
while l_cur_var = '\' loop
l_count_quotes := l_count_quotes +1;
l_cur_pos := l_cur_pos-1;
l_cur_var := dbms_lob.substr(p_value,1,l_cur_pos-1);
end loop;
if(mod(l_count_quotes,2) = 0) then
l_is_end := 1;
end if;
end loop;
p_string := dbms_lob.substr(p_value,p_offset-2,2);
-- dbms_output.put_line('get_q_string:'||p_offset|| ':'||length(p_string) );
end;
procedure get_one_bracket(p_value clob,
p_bracket varchar2 := '[]',
p_offset out number) is
l_left varchar2(1 char) := substr(p_bracket,1,1);
l_right varchar2(1 char) := substr(p_bracket,2,1);
l_cur_pos number;
begin
l_cur_pos := 1;
p_offset := dbms_lob.instr(p_value,l_right);
l_cur_pos := dbms_lob.instr(p_value,l_left,l_cur_pos+1);
while l_cur_pos > 0 and l_cur_pos < p_offset loop
l_cur_pos := dbms_lob.instr(p_value,l_left,l_cur_pos+1);
p_offset := dbms_lob.instr(p_value,l_right,p_offset+1);
if p_offset < 1 then
p_offset := dbms_lob.getlength(p_value);
exit;
end if;
end loop;
-- p_offset := p_offset-1;
-- dbms_output.put_line('get_one_bracket:'|| p_bracket||':'||p_offset );
end;
procedure parse_json (p_clob clob
, p_parent number) is
l_pos number := 1;
l_offset number := 1;
l_length number := dbms_lob.getlength(p_clob);
l_cur_char varchar2(1 char);
l_rec t_str_rec := t_str_rec();
l_parent number := p_parent;
begin
while(l_pos < l_length) loop
l_cur_char := dbms_lob.substr(p_clob, 1,l_pos);
if l_cur_char = '"' then
l_rec.extend();
get_q_string(dbms_lob.substr(p_clob, l_length-l_pos, l_pos),
l_offset,
l_rec(l_rec.last));
elsif l_cur_char = ':' then null; -- it's just a separator
elsif l_cur_char IN ('0','1','2','3','4','5','6','7','8','9') then
l_rec.extend();
get_number (dbms_lob.substr(p_clob, l_length-l_pos, l_pos),
l_offset,
l_rec(l_rec.last));
elsif l_cur_char = '{' then
l_rec.extend();
l_rec(l_rec.last) := '{}';
get_one_bracket(dbms_lob.substr(p_clob, l_length-l_pos+1, l_pos),
'{}',
l_offset);
declare
l_id number := l_count;
begin
parse_json (dbms_lob.substr(p_clob, l_offset, l_pos+1),
l_id);
end;
elsif l_cur_char = '[' then
l_rec.extend();
l_rec(l_rec.last) := '[]';
get_one_bracket(dbms_lob.substr(p_clob, l_length-l_pos+1, l_pos),
'[]',
l_offset);
l_rec.extend();
l_count := l_count + 1;
l_rec(l_rec.last) := l_count;
l_rec.extend();
l_rec(l_rec.last) := l_parent;
l_tab.extend;
l_tab(l_tab.last) := l_rec;
l_rec := t_str_rec();
declare
l_id number := l_count;
begin
parse_json (dbms_lob.substr(p_clob, l_offset, l_pos+1),
l_id);
end;
elsif l_cur_char = ',' then
IF dbms_lob.substr(p_clob, 1,l_pos-1) NOT IN (']','}',',') then
l_rec.extend();
l_count := l_count + 1;
l_rec(l_rec.last) := l_count;
l_rec.extend();
l_rec(l_rec.last) := l_parent;
l_tab.extend;
l_tab(l_tab.last) := l_rec;
l_rec := t_str_rec();
end if;
end if;
l_pos := l_pos+l_offset;
l_offset := 1;
end loop;
l_rec.extend();
l_count := l_count + 1;
l_rec(l_rec.last) := l_count;
l_rec.extend();
l_rec(l_rec.last) := l_parent;
l_tab.extend;
l_tab(l_tab.last) := l_rec;
l_rec := t_str_rec();
end;
begin
parse_json(
'{
"Version": 1,
"nodes": [{
"id": 0,
"Fields": ["ABC"],
"nodes": [{
"id": 1,
"Fields": ["DEF"],
"nodes": [{
"id": 2,
"Fields": ["GHI", "HIG"],
"nodes": []
}
]
}, {
"id": 3,
"Fields": ["XYZ", "YZX"],
"nodes": [{
"id": 4,
"Fields": ["UVW"],
"nodes": [{
"id": 5,
"Fields": ["RST"],
"nodes": []
}
]
}
]
}
]
}
]
}',l_count);
declare
v_vers varchar2(10);
v_id varchar2(10) ;
v_field varchar2(4000);
v_del varchar2(1);
v_is_field number;
v_parent varchar2(10);
type t_num_decode is table of varchar2(10) index by varchar2(10);
l_num_decode t_num_decode;
begin
for ba in l_tab.first .. l_tab.last loop
if v_vers is null and l_tab(ba)(1) = 'Version' then
v_vers := l_tab(ba)(2);
end if;
if l_tab(ba)(1) = 'id' then
v_id := l_tab(ba)(2);
elsif l_tab(ba)(1) = 'Fields' then
v_is_field := 1;
v_field := '';
elsif l_tab(ba)(1) = 'nodes' and v_id is not null then
if l_num_decode.exists(l_tab(ba)(4)) then
v_parent := l_num_decode(l_tab(ba)(4));
end if;
dbms_output.put_line(v_vers||','||v_id||','||v_field||','||nvl(v_parent,'null'));
l_num_decode(l_tab(ba)(3)) := v_id;
v_is_field := 0;
v_del := '';
elsif v_is_field = 1 then
v_field := v_field||v_del||l_tab(ba)(1);
v_del := '|';
end if;
end loop;
end;
end;
/