Oracle introduced support for json in 12.1, you can use sql to query the value of each node in the json field, for the 11G version, such as the EBS environment, it is not convenient to upgrade to 12C, you can use the open source software pljson to achieve, this article uses ORDS + pljson to implement the development of a post example request to receive order data.
involving software components
ORDS 20.2
pljson 3.5.2
Oracle dbms 11GR2
Oracle apex 20.1
json data source instance
The following paragraph is the json to be processed, which is a multi-level organization, including order header information and two order lines
{
"PONumber": 1608,
"Requestor": "Alexis Bull",
"CostCenter": "A50",
"Address": {
"street": "200 Sporting Green",
"city": "South San Francisco",
"state": "CA",
"zipCode": 99236,
"country": "United States of America"
},
"LineItems": [
{
"ItemNumber": 1,
"Part": {
"Description": "One Magic Christmas",
"UnitPrice": 19.95,
"UPCCode": 1313109289
},
"Quantity": 9.0
},
{
"ItemNumber": 2,
"Part": {
"Description": "Lethal Weapon",
"UnitPrice": 19.95,
"UPCCode": 8539162892
},
"Quantity": 5.0
}
]
}
Data Source Reference Documentation
Technical realization
In the official Oracle document above, the json_table function is used. This function is similar in syntax to the xmltable function of 11G. This is a new function only available after 12C. It is very convenient for processing json strings, but for versions before 12.1 Without this function, Oracle is always a step behind. Before 12C, everyone had a strong demand for json, so there was an open source tool pljson. This plug-in essentially uses some Oracle collections.
However, open source is not as easy to use as the native implementation from the bottom layer. For example, there is a query function like the following code:
select *
from table(
pljson_table.json_table(
'{"data":
{
"name": "name 3",
"description": "blah blah...",
"type": "text",
"created_time": "2015-12-21T19:23:29+0000",
"shares": { "count": 100 },
"extra": null,
"maps" : [ true, true, false ]
}
}',
pljson_varray('data.name', 'data.extra', 'data.maps', 'data.shares.count', 'data.missing'),
pljson_varray('name', 'extra', 'map', 'count', 'whatelse'))
)
As a sql script, there is no problem when running in the sql engine, but under the pl/sql engine, an error ORA-22905 cannot be accessed from a non-nested table item. The essence is that when using a table, it needs to be in the pl/sql block. Declare what type is in the table. However, the return value of pljson_table.json_table is an anydataset, and this Oracle ANYDATASET type has another way of playing, so I will not study how to use this in pl/sql for the time being. To change the way of thinking, we can encapsulate the above query into a view, which uses the sql engine, and then query the view in pl/sql.
official code
We essentially want to achieve the following effects, the following code comes from Oracle's official ORDS documentation.
Declare
L_PO BLOB;
Begin
L_PO := :body;
INSERT INTO PurchaseOrder
SELECT * FROM json_table(L_PO FORMAT JSON, '$'
COLUMNS (
PONo Number PATH '$.PONumber',
Requestor VARCHAR2 PATH '$.Requestor',
CostCenter VARCHAR2 PATH '$.CostCenter',
AddressStreet VARCHAR2 PATH '$.Address.street',
AddressCity VARCHAR2 PATH '$.Address.city',
AddressState VARCHAR2 PATH '$.Address.state',
AddressZip VARCHAR2 PATH '$.Address.zipCode',
AddressCountry VARCHAR2 PATH '$.Address.country'));
INSERT INTO LineItem
SELECT * FROM json_table(L_PO FORMAT JSON, '$'
COLUMNS (
PONo Number PATH '$.PONumber',
NESTED PATH '$.LineItems[*]'
COLUMNS (
ItemNumber Number PATH '$.ItemNumber',
PartDescription VARCHAR2 PATH '$.Part.Description',
PartUnitPrice Number PATH '$.Part.UnitPrice',
PartUPCCode Number PATH '$.Part.UPCCode',
Quantity Number PATH '$.Quantity')));
commit;
end;
json_table(L_PO FORMAT JSON This function is very powerful, and it realizes many things at once, such as the conversion from blob to clob, and the definition of collection.
For this reason, we need to implement blob to clob conversion by ourselves, because the input parameter received by pljson must be of clob type.
Table Structure
The table structure is as follows:
--接收POST过来原始数据的表
-- Create table
create table CUX_APEX_JSON_CLOB_DATA
(
request_id NUMBER not null,
json_lob CLOB,
creation_date DATE default SYSDATE
);
-- Create/Recreate indexes
create unique index CUX_APEX_JSON_CLOB_DATA_U1 on CUX_APEX_JSON_CLOB_DATA (REQUEST_ID);
--序列
create sequence CUX_APEX_JSON_CLOB_DATA_S;
-- Create table 订单头表
create table PURCHASEORDER
(
pono NUMBER(5) not null,
requestor VARCHAR2(50),
costcenter VARCHAR2(5),
addressstreet VARCHAR2(50),
addresscity VARCHAR2(50),
addressstate VARCHAR2(2),
addresszip VARCHAR2(10),
addresscountry VARCHAR2(50),
request_id NUMBER not null
);
-- Create/Recreate primary, unique and foreign key constraints
alter table PURCHASEORDER
add primary key (PONO)
;
-- Create table 订单行表
create table LINEITEM
(
pono NUMBER(5) not null,
itemnumber NUMBER(10) not null,
partdescription VARCHAR2(50),
partunitprice NUMBER(10),
partupccode NUMBER(10),
quantity NUMBER(10),
qequest_id NUMBER not null
);
-- Create/Recreate primary, unique and foreign key constraints
alter table LINEITEM
add primary key (PONO, ITEMNUMBER);
bloc_to_clob conversion function
CREATE OR REPLACE FUNCTION blob_to_clob(blob_in IN BLOB) RETURN CLOB AS
v_clob CLOB;
v_varchar VARCHAR2(32767);
v_start PLS_INTEGER := 1;
v_buffer PLS_INTEGER := 32767;
BEGIN
DBMS_LOB.CREATETEMPORARY(v_clob, TRUE);
FOR i IN 1 .. CEIL(DBMS_LOB.GETLENGTH(blob_in) / v_buffer) LOOP
v_varchar := UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(blob_in,
v_buffer,
v_start));
DBMS_LOB.WRITEAPPEND(v_clob, LENGTH(v_varchar), v_varchar);
--DBMS_OUTPUT.PUT_LINE(v_varchar);
v_start := v_start + v_buffer;
END LOOP;
RETURN v_clob;
END blob_to_clob;
view
--------------------需要注意,视图select字段必须大写----------------------------------
--头表视图
CREATE OR REPLACE VIEW DEMO_HEADER_V AS
SELECT js."PONO",
js."REQUESTOR",
js."COSTCENTER",
js."ADDRESSSTREET",
js."ADDRESSCITY",
js."ADDRESSSTATE",
js."ADDRESSZIP",
js."ADDRESSCOUNTRY",
tab.request_id
FROM cux_apex_json_clob_data tab,
TABLE(pljson_table.json_table(tab.json_lob,
pljson_varray('PONumber',
'Requestor',
'CostCenter',
'Address.street',
'Address.city',
'Address.state',
'Address.zipCode',
'Address.country'),
pljson_varray('pono',
'requestor',
'costcenter',
'addressstreet',
'addresscity',
'addressstate',
'addresszip',
'addresscountry'))) js;
--行表视图
CREATE OR REPLACE VIEW DEMO_LINE_V AS
SELECT js."PONO",
js."ITEMNUMBER",
js."PARTDESCRIPTION",
js."PARTUNITPRICE",
js."PARTUPCCODE",
js."QUANTITY",
pljtt.request_id
FROM cux_apex_json_clob_data pljtt,
TABLE(pljson_table.json_table(pljtt.json_lob,
pljson_varray('PONumber',
'LineItems[*].ItemNumber',
'LineItems[*].Part.Description',
'LineItems[*].Part.UnitPrice',
'LineItems[*].Part.UPCCode',
'LineItems[*].Quantity'),
pljson_varray('pono',
'itemnumber',
'partdescription',
'partunitprice',
'partupccode',
'quantity'),
table_mode => 'nested')) js;
pl/sql code
CREATE OR REPLACE PACKAGE cux_apex_json_demo_pkg IS
PROCEDURE create_order(p_body IN BLOB);
END cux_apex_json_demo_pkg;
/
CREATE OR REPLACE PACKAGE BODY cux_apex_json_demo_pkg IS
--******************************************************************************
-- FUNCTION get_tax_rate
--
-- p_body POST请求过来的BLOB数据
--
-- Public. demo演示,获取POST过来的json数据,将其入库
--
--******************************************************************************
PROCEDURE create_order(p_body IN BLOB)
IS
c_body CLOB := blob_to_clob(p_body);
l_request_id NUMBER := cux_apex_json_clob_data_s.nextval;
BEGIN
INSERT INTO cux_apex_json_clob_data
VALUES
(l_request_id, c_body, SYSDATE);
plog.debug(l_request_id || '-insert cux_apex_json_clob_data rows:' || SQL%ROWCOUNT);
INSERT INTO PurchaseOrder
SELECT *
FROM demo_header_v
WHERE request_id = l_request_id;
plog.debug(l_request_id || '-insert PurchaseOrder rows:' || SQL%ROWCOUNT);
INSERT INTO LineItem
SELECT *
FROM demo_line_v
WHERE request_id = l_request_id;
plog.debug(l_request_id || '-insert LineItem rows:' || SQL%ROWCOUNT);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
plog.full_error_backtrace;
RAISE;
END create_order;
BEGIN
NULL;
END cux_apex_json_demo_pkg;
/
Encapsulated into ORDS
BEGIN
ORDS.ENABLE_SCHEMA(
p_enabled => TRUE,
p_schema => 'CUX_APEX',
p_url_mapping_type => 'BASE_PATH',
p_url_mapping_pattern => 'CUX_APEX',
p_auto_rest_auth => FALSE);
ORDS.DEFINE_MODULE(
p_module_name => 'demo',
p_base_path => '/demo/',
p_items_per_page => 25,
p_status => 'PUBLISHED',
p_comments => 'json多行测试');
ORDS.DEFINE_TEMPLATE(
p_module_name => 'demo',
p_pattern => 'test',
p_priority => 0,
p_etag_type => 'HASH',
p_etag_query => NULL,
p_comments => NULL);
ORDS.DEFINE_HANDLER(
p_module_name => 'demo',
p_pattern => 'test',
p_method => 'POST',
p_source_type => 'plsql/block',
p_items_per_page => 0,
p_mimes_allowed => '',
p_comments => NULL,
p_source =>
'BEGIN
cux_apex_json_demo_pkg.create_order(:BODY);
END;
');
COMMIT;
END;
The definition in aepx is as follows:
additional update
The above example needs to put json data into the database. In fact, if dynamic sql is used, json can be passed in as a binding variable, so as to avoid inserting redundant data, but the original json data passed in will be lost.
--json内容
{
"DATA":
[{
"storeId":"1","supplierId":"111","takeCost":"1.00","takeDate":"20191112"},
{
"storeId":"2","supplierId":"2","takeCost":"200.00","takeDate":"20191114"},
{
"storeId":"2","supplierId":"3","takeCost":"100.00","takeDate":"20191111"}]
}
--动态sql
execute immediate q'{ insert into supp_cost_t(storeid,
supplierid,
takecost,
takedate)
select b.storeid, b.supplierId, b.takeCost, b.takeDate
from table(pljson_table.json_table(:1,
pljson_varray('DATA[*].storeId',
'DATA[*].supplierId',
'DATA[*].takeCost',
'DATA[*].takeDate'),
pljson_varray('storeId',
'supplierId',
'takeCost',
'takeDate'),
table_mode => 'nested')) b}'
using json_CLOB;
Reference blog:
https://darkathena.blog.csdn.net/article/details/120644329?spm=1001.2014.3001.5502