I want to invoke a restful API (JSON) in plsql. The JSON Payload is as follows:
{
"data": {
"content": "encrypted content", "signature": "JKQWJK34K32JJEK2JQWJ5678",
"dataDescription": {
"codeType": "0",
"encryptCode": "1",
"zipCode": "0"
}
},
"globalInfo": {
"appId": "AP01",
"version": "1.1.20191201",
"dataExchangeId": "9230489223014123",
"interfaceCode": "T101",
"requestCode": "TP",
"requestTime": "2019-06-11 17:07:07",
"responseCode": "TA",
"userName": "admin",
"deviceMAC": "FFFFFFFFFFFF",
"deviceNo": "00022000634",
"tin": "1009830865",
5
"brn": "",
"taxpayerID": "1",
"longitude": "116.397128",
"latitude": "39.916527",
"extendField": {
"responseDateFormat": "dd/MM/yyyy",
"responseTimeFormat": "dd/MM/yyyy HH:mm:ss"
}
},
"returnStateInfo": {
"returnCode": "",
"returnMessage": ""
}
}
The Interface Codes (interfaceCode: T101) are defined, and for each method there is an interface Code, I just want to know how do i invoke a interface Method?
You can do it with UTL_HTTP package.
declare
v_req utl_http.req;
v_res utl_http.resp;
v_buffer varchar2(4000);
v_body varchar2(4000) := '{"field":"value"}'; -- Your JSON
begin
-- Set connection.
v_req := utl_http.begin_request('http://your.api/operation', 'POST');
utl_http.set_authentication(v_req, 'your_username','your_password');
utl_http.set_header(v_req, 'content-type', 'application/json');
utl_http.set_header(v_req, 'Content-Length', length(v_body));
-- Invoke REST API.
utl_http.write_text(v_req, v_body);
-- Get response.
v_res := utl_http.get_response(v_req);
begin
loop
utl_http.read_line(v_res, v_buffer);
-- Do something with buffer.
dbms_output.put_line(v_buffer);
end loop;
utl_http.end_response(v_res);
exception
when utl_http.end_of_body then
utl_http.end_response(v_res);
end;
end;
But if your database has got APEX installed, then you can try APEX_WEB_SERVICE package (much simpler).
declare
v_response clob;
v_buffer varchar2(32767);
v_buffer_size number := 32000;
v_offset number := 1;
begin
-- Set connection and invoke REST API.
v_response := apex_web_service.make_rest_request(
p_url => 'http://your.api/operation',
p_http_method => 'POST',
p_username => 'your_username',
p_password => 'your_password',
p_body => '{"field":"value"}' -- Your JSON.
);
-- Get response.
begin
loop
dbms_lob.read(v_response, v_buffer_size, v_offset, v_buffer);
-- Do something with buffer.
DBMS_OUTPUT.PUT_LINE(v_buffer);
v_offset := v_offset + v_buffer_size;
end loop;
exception
when no_data_found then
null;
end;
end;
If you get an ACL exception, then you have to create ACL to open TCP port to connect with REST API.
BEGIN
DBMS_NETWORK_ACL_ADMIN.create_acl (
acl => 'acl.xml',
description => 'Connecting with REST API',
principal => 'YOUR_DATABASE_SCHEMA',
is_grant => TRUE,
privilege => 'connect',
start_date => SYSTIMESTAMP,
end_date => NULL
);
DBMS_NETWORK_ACL_ADMIN.assign_acl (
acl => 'acl.xml',
host => 'localhost', -- Or hostname of REST API server (e.g. "example.com").
lower_port => 80, -- For HTTPS put 443.
upper_port => NULL
);
COMMIT;
end;
I assume your REST API is protected by basic authentication scheme (user and password). To keep this example simple i used HTTP. If you have to connect via HTTPS, then you have to change TCP port in ACL and configure Oracle Wallet for your Oracle database instance.
Related
For a Security product I am currently writing in Delphi. I am using the below solution to use the ARP table to get MAC-Addresses from devices to detected what is on the network.
How to find MAC addresses from arp -a scan
I just do a range of Ping commands to fill the ARP table and read results from the ARP-table.
However when a Firewall on a machine is blocking Ping. Sometimes the MAC is still exposed in ARP, but not always. What is a better solution to detect all devices on a network and get MAC-Addresses from them?
//-----------------------------------------------------------------------------
{ ARP-table lists relations between remote IP and remote MAC-address.
NOTE: these are cached entries;when there is no more network traffic to a
node, entry is deleted after a few minutes.
}
//-----------------------------------------------------------------------------
procedure Get_ARPTable( aList: TStrings; aDeviceList : TObjectList<TNetworkDevice>);
var
lIPNetRow : TMibIPNetRow;
lTableSize : DWORD;
lNumEntries : DWORD;
lErrorCode : DWORD;
lIdx : Integer;
lPBuf : PAnsiChar;
lPhysAddr : TMACAddress;
lMacAddr2Str : string;
ldwAddr : string;
ldwType : string;
lNewDevice : TNetworkDevice;
begin
if not LoadIpHlp then Exit;
if not Assigned( aList ) then Exit;
if not Assigned( aDeviceList) then Exit;
Get_LocalNetworkDevices(aDeviceList);
aList.Clear;
lTableSize := 0;
lErrorCode := GetIPNetTable( Nil, #lTableSize, False );
//
if lErrorCode = ERROR_NO_DATA then
begin
aList.Add( ' ARP-cache empty.' );
EXIT;
end;
// get table
GetMem( lPBuf, lTableSize );
lNumEntries := 0;
try
lErrorCode := GetIpNetTable( PTMIBIPNetTable( lPBuf ), #lTableSize, False );
if lErrorCode = NO_ERROR then
begin
lNumEntries := PTMIBIPNetTable( lPBuf )^.dwNumEntries;
if lNumEntries > 0 then
begin
Inc( lPBuf, SizeOf( DWORD ) );
for lIdx := 1 to lNumEntries do
begin
lIPNetRow := PTMIBIPNetRow( lPBuf )^;
lMacAddr2Str := MacAddr2Str( lIPNetRow.bPhysAddr, lIPNetRow.dwPhysAddrLen );
lPhysAddr := lIPNetRow.bPhysAddr;
ldwAddr := IPAddr2StrTrunc(lIPNetRow.dwAddr);
ldwType := ARPEntryType[lIPNetRow.dwType];
lNewDevice := SeekDevice(aDeviceList, lMacAddr2Str);
if Assigned(lNewDevice) then
begin
lNewDevice.IP := ldwAddr;
lNewDevice.IsNew := False;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
if (lNewDevice.EntryType = 'Dynamic') or
(lNewDevice.EntryType = 'Static') then
lNewDevice.SetStamp;
end
else
begin
lNewDevice := TNetworkDevice.Create;
lNewDevice.IP := ldwAddr;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
lNewDevice.AddOrUpdate(lMacAddr2Str);
lNewDevice.SetFirstSeen;
lNewDevice.SetStamp;
lNewDevice.State := dtRogue;
lNewDevice.IsNew := True;
aDeviceList.Add(lNewDevice);
end;
with lIPNetRow do
begin
aList.Add( Format( '%8x | %12s | %16s| %10s',
[dwIndex, MacAddr2Str( bPhysAddr, dwPhysAddrLen ),
IPAddr2Str( dwAddr ), ARPEntryType[dwType]
]));
end;
Inc( lPBuf, SizeOf( lIPNetRow ) );
end;
end
else
aList.Add( ' ARP-cache empty.' );
end
else
aList.Add( SysErrorMessage( lErrorCode ) );
// we _must_ restore Pointer!
finally
Dec( lPBuf, SizeOf( DWORD ) + lNumEntries * SizeOf( lIPNetRow ) );
FreeMem( lPBuf );
end;
end;
To complement ping, you can try to establish a TCP connection on any port, for example port 80 (HTTP). Of course you'll get mostly connection denied but you'll still be able to find an entry in the ARP table.
Depending on the firewall, you may also simply get to response at all. Then you have to try another port. I would suggest 135 (RPC) or 445 or 139 (Both SMB) which should be open is network shares are enabled. Another one in 3389 used by RDP if enabled.
This is practical only if the subnet is small (Class-C, 256 addresses) or medium (Class-B, 16K addresses). Anyway, it would take a lot of time.
I am getting JSON data after consuming an API in my stored procedure. I am trying to parse this JSON but while trying to parse it, I get an error
JSON path is not properly formatted. Unexpected character '#' is found at position 2
I was wondering how can I pass this #variant_description dynamic variable to my parser?
Thank you in advance
ALTER PROCEDURE [dbo].[usp_CallAPI]
#genome_build nvarchar(50),
#variant_description nvarchar(50),
#select_transcripts nvarchar(50)
AS
BEGIN
DECLARE #Object AS Int;
DECLARE #hr int, #APIfunction nvarchar(max)
DECLARE #json AS TABLE (Json_Table nvarchar(max))
DECLARE #Json_response AS nvarchar(max)
SET #APIfunction = 'https://rest.variantvalidator.org/VariantValidator/variantvalidator/'+ #genome_build+'/'+#variant_description+'/'+#select_transcripts
EXEC #hr = sp_OACreate 'MSXML2.ServerXMLHTTP.6.0', #Object OUT;
IF #hr <> 0
EXEC sp_OAGetErrorInfo #Object
EXEC #hr = sp_OAMethod #Object, 'open', NULL, 'get', #APIfunction
, --Your Web Service Url (invoked)
'false'
IF #hr <> 0
EXEC sp_OAGetErrorInfo #Object
EXEC #hr = sp_OAMethod #Object, 'send'
IF #hr <> 0
EXEC sp_OAGetErrorInfo #Object
EXEC #hr = sp_OAMethod #Object, 'responseText', #json OUTPUT
IF #hr <> 0
EXEC sp_OAGetErrorInfo #Object
INSERT INTO #json (Json_Table)
EXEC sp_OAGetProperty #Object, 'responseText'
-- select the JSON string
-- SELECT '"elements": ['+Json_Table+ ']' FROM #json
-- SELECT * FROM #json
-- Parse the JSON string
SELECT *
FROM OPENJSON((SELECT CONCAT('{', QUOTENAME('elements', '"'), ':['+Json_Table+ ']}') FROM #json), N'$.elements')
WITH (
-- [seqrepo_db] nvarchar(max) N'$.seqrepo_db' ,
[variant_description] nvarchar(max) N'$.#variant_description' AS JSON,
[flag] nvarchar(max) N'$.flag',
[metadata] nvarchar(max) N'$.metadata' AS JSON
)
EXEC sp_OADestroy #Object
END
For example, I want to execute the following
EXEC [dbo].usp_CallAPI #genome_build ='GRCh37',#variant_description = 'NM_000088.3:c.589G>T', #select_transcripts ='all';
For example, this is the test JSON which I am getting back from the API request
{
"NM_000088.3:c.589G>T": {
"alt_genomic_loci": [],
"gene_ids": {
"ccds_ids": [
"CCDS11561"
],
"ensembl_gene_id": "ENSG00000108821",
"entrez_gene_id": "1277",
"hgnc_id": "HGNC:2197",
"omim_id": [
"120150"
],
"ucsc_id": "uc002iqm.4"
},
"gene_symbol": "COL1A1",
"genome_context_intronic_sequence": "",
"hgvs_lrg_transcript_variant": "LRG_1t1:c.589G>T",
"hgvs_lrg_variant": "LRG_1:g.8638G>T",
"hgvs_predicted_protein_consequence": {
"slr": "NP_000079.2:p.(G197C)",
"tlr": "NP_000079.2(LRG_1p1):p.(Gly197Cys)"
},
"hgvs_refseqgene_variant": "NG_007400.1:g.8638G>T",
"hgvs_transcript_variant": "NM_000088.3:c.589G>T",
"primary_assembly_loci": {
"grch37": {
"hgvs_genomic_description": "NC_000017.10:g.48275363C>A",
"vcf": {
"alt": "A",
"chr": "17",
"pos": "48275363",
"ref": "C"
}
},
"grch38": {
"hgvs_genomic_description": "NC_000017.11:g.50198002C>A",
"vcf": {
"alt": "A",
"chr": "17",
"pos": "50198002",
"ref": "C"
}
},
"hg19": {
"hgvs_genomic_description": "NC_000017.10:g.48275363C>A",
"vcf": {
"alt": "A",
"chr": "chr17",
"pos": "48275363",
"ref": "C"
}
},
"hg38": {
"hgvs_genomic_description": "NC_000017.11:g.50198002C>A",
"vcf": {
"alt": "A",
"chr": "chr17",
"pos": "50198002",
"ref": "C"
}
}
},
"reference_sequence_records": {
"lrg": "http://ftp.ebi.ac.uk/pub/databases/lrgex/LRG_1.xml",
"protein": "https://www.ncbi.nlm.nih.gov/nuccore/NP_000079.2",
"refseqgene": "https://www.ncbi.nlm.nih.gov/nuccore/NG_007400.1",
"transcript": "https://www.ncbi.nlm.nih.gov/nuccore/NM_000088.3"
},
"refseqgene_context_intronic_sequence": "",
"submitted_variant": "NM_000088.3:c.589G>T",
"transcript_description": "Homo sapiens collagen type I alpha 1 chain (COL1A1), mRNA",
"validation_warnings": []
},
"flag": "gene_variant",
"metadata": {
"seqrepo_db": "2018-08-21",
"uta_schema": "uta_20180821",
"variantvalidator_hgvs_version": "1.2.5.vv1",
"variantvalidator_version": "1.0.4.dev17+gd16b9ef.d20200422"
}
}
I don't think that you can use a variable for a path expression, when you use OPENJSON() and explicit schema (the WITH clause). But, if the JSON response has a fixed structure - a JSON object (not array) with three key\value pairs (first with a variable name, "flag" and "metadata"), you may try to change the way you are parsing the JSON and use JSON_VALUE() and JSON_QUERY(). In this case, starting from SQL Server 2017, you can provide a variable as the value of path. Also, you do not need to build a new JSON object ({"elements:[...]"}), simply parse the returned JSON response.
Use the following statement at the end of your stored procedure:
SELECT
variant_description = JSON_QUERY(#json, '$."' + #variant_description + '"'),
flag = JSON_VALUE(#json, '$."flag"'),
metadata = JSON_QUERY(#json, '$."metadata"')
instead of:
SELECT *
FROM OPENJSON(
(SELECT CONCAT('{', QUOTENAME('elements', '"'), ':['+Json_Table+ ']}') FROM #json),
N'$.elements'
) WITH (
-- [seqrepo_db] nvarchar(max) N'$.seqrepo_db' ,
[variant_description] nvarchar(max) N'$.#variant_description' AS JSON,
[flag] nvarchar(max) N'$.flag',
[metadata] nvarchar(max) N'$.metadata' AS JSON
)
As a side note (and for your future work), the actual reason for the "JSON path is not properly formatted. Unexpected character '#' is found at position 2" error is the fact, that you use N'$.#variant_description' as a path expression. As is explained in the documentation, if ... the key name starts with a dollar sign or contains special characters such as spaces, surround it with quotes (e.g. N'$."#variant_description"').
I write some code (rest server) that produce for me data in JSON format. When I use it in PHP it works fine, JSON is valid, everything is ok. When I use it in Delphi nothing works.
When I search internet I found:
desearilizing JSON using SuperObject
but that method returns empty strings for me.
I want to use that JSON elements as array (eg. JSONValue.items[i]).
I'm using Delphi XE7 System.JSON and don't want to use superobject or any others libraries.
How use it as array?
I paste my code that generates JSON:
var
qry: TfdQuery;
FieldsObj: TJSONObject;
FieldNameArray: TJSONArray;
I: Integer;
DataObj: TJSONObject;
DataRows: TJSONArray;
RowFields: TJSONArray;
tablename:string;
begin
tablename:='produkt';
qry := TfdQuery.Create(Self);
qry.SQL.Text := 'select * from produkt where (id ='''+ProductID+''')';
qry.Connection := FDConnection1;
qry.Open;
FieldsObj := TJSONObject.Create;
FieldNameArray := TJSONArray.Create;
for I := 0 to qry.FieldCount - 1 do
FieldNameArray.Add(qry.Fields[I].FieldName);
FieldsObj.AddPair(TableName, FieldNameArray);
DataObj := TJSONObject.Create;
DataRows := TJSONArray.Create;
qry.First;
while not qry.Eof do
begin
RowFields := TJSONArray.Create;
for I := 0 to qry.FieldCount - 1 do
RowFields.Add(qry.Fields[I].AsString);
DataRows.Add(RowFields);
qry.Next;
end;
DataObj.AddPair('data', DataRows);
Result := TJSONArray.Create(FieldsObj, DataObj);
qry.Free;
And this is the result:
{
"ProductID": "1",
"result": [{
"produkt": ["id", "parent_id", "full_name", "opcja_1", "opcja_2", "opcja_3", "opcja_4", "opcja_5", "opcja_6", "opcja_7", "opcja_8", "opcja_9", "opcja_10", "opcja_11", "opcja_12", "field_address1", "field_address2", "quantity", "opis", "zdjecie1", "zdjecie2", "zdjecie3", "samples", "link_stable0", "link_stable1", "link_stable2", "price1", "price2", "price3"]
}, {
"data": [
["1", "1", "name", "1", "1", "1", "1", "0", "0", "0", "0", "0", "0", "0", "12", "10", "20", "1,2", "description of product", "http://www.vphosted.com/e6=0", "photo link2", "photo link 3", "sample project file link", "link option", "10", "link", "10", "link", "10"]
]
}]
}
This would produce JSON more in the format that I would expect:
var
qry: TfdQuery;
FieldsObj: TJSONObject;
//FieldNameArray: TJSONArray;
I: Integer;
DataObj: TJSONObject;
FieldObj: TJSONObject;
DataRows: TJSONArray;
RowFields: TJSONArray;
tablename:string;
begin
tablename:='produkt';
qry := TfdQuery.Create(Self);
qry.SQL.Text := 'select * from produkt where (id ='''+ProductID+''')';
qry.Connection := FDConnection1;
qry.Open;
FieldsObj := TJSONObject.Create;
//FieldNameArray := TJSONArray.Create;
//for I := 0 to qry.FieldCount - 1 do
// FieldNameArray.Add(qry.Fields[I].FieldName);
//FieldsObj.AddPair(TableName, FieldNameArray);
DataObj := TJSONObject.Create;
DataRows := TJSONArray.Create;
qry.First;
while not qry.Eof do
begin
RowFields := TJSONArray.Create;
for I := 0 to qry.FieldCount - 1 do
begin
FieldObj := TJSONObject.Create;
FieldObject.AddPair(qry.Fields[I].FieldName, qry.Fields[I].AsString));
RowFields.Add( FieldObj );
end;
DataRows.Add(RowFields);
qry.Next;
end;
DataObj.AddPair('data', DataRows);
Result := TJSONArray.Create(FieldsObj, DataObj);
qry.Free;
If you know the record structure, though, I would prefer to use REST.JSON, which I am pretty sure ships with XE7 and is much simpler to use. You just create your object structure, create an instance of that structure, populate it with your field values and use
TJSON.ObjectToJsonString( fObject )
to create your string and
iObject := TJSON.JsonToObject<TMyObject>( pTransferString );
to get your object back.
If you want a more complete example using this method, let me know and I will post.
I am working on parsing JSON CLOB in oracle. I am trying to retrieve and parse individual JSON elements.
Main issue I isolated is that compiler is unable recognize the JSON type here. However, I am able to insert and access individual JSON elements in the po_document field(declared as CLOB). This is the JSON I am trying to access
'{"PONumber" : 1600,
"Reference" : "ABULL-20140421",
"Requestor" : "Alexis Bull",
"User" : "ABULL",
"CostCenter" : "A50",
"ShippingInstructions" : "no such",
"Special Instructions" : null,
"AllowPartialShipment" : true,
"LineItems" : "no line"}'
I just took a standard Pl/SQL block to parse the JSON object:
DECLARE
vCONTENT CLOB;
v_parent_json json;
v_json_message_list json_list;
v_json_message_list_value json_value;
v_parent_json_value json_value;
BEGIN
SELECT po_document INTO vCONTENT FROM j_purchaseorder;
v_parent_json := json(vCONTENT);
v_parent_json := json(v_parent_json.get(1));
v_json_message_list := json_list(v_parent_json.get('LineItems'));
DBMS_OUTPUT.PUT_LINE(v_json_message_list.count);
for message_loop_counter in 1 ..v_json_message_list.count loop
v_parent_json_value := json(v_json_message_list.get(message_loop_counter)).get(1);
DBMS_OUTPUT.PUT_LINE(v_parent_json_value.mapname);
END LOOP;
END;
The compiler log generates the error message: Error(3,8): PLS-00201: identifier 'JSON' must be declared
Output from v$version:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
I was trying different things initially. I was using PL/JSON functions in my questions but if I want to use Oracle functions, this could be a small demo to read JSON and print values:
declare
l_has_data_level_co BOOLEAN := FALSE; -- TRUE is for R12C
jdemo CLOB;
l_name varchar2(2000):='';
l_location varchar2(2000):='';
begin
jdemo := '{"PONumber" : 1600,
"Reference" : "ABULL-20140421",
"Requestor" : "Alexis Bull",
"User" : "ABULL",
"CostCenter" : "A50",
"ShippingInstructions" : "no such",
"Special Instructions" : null,
"AllowPartialShipment" : true,
"LineItems" : "no line"}';
SELECT
json_value(jdemo, '$.PONumber'),
json_value(jdemo, '$.Reference')
into
l_name,
l_location
FROM dual;
--DBMS_OUTPUT.PUT_LINE (SYSDATE||' '||jdemo);
DBMS_OUTPUT.PUT_LINE ('Name :'||l_name||' Location :'||l_location);
end;
Is there an easy way to work with JSON within oracle? I have a standard procedure that I use to call web services quite often, JSON is a format that I am familiar with in web development context, but what is the best way to work with json within a stored procedure? For instance take the CLOB response from the URI, convert that to a JSON object and get a value from that?
For reference sake, here is the procedure I used to fetch URLs
create or replace procedure macp_URL_GET(url_resp in out clob, v_url in varchar2) is
req Utl_Http.req;
resp Utl_Http.resp;
NAME VARCHAR2 (255);
VALUE VARCHAR2 (1023);
v_msg VARCHAR2 (80);
v_ans clob;
-- v_url VARCHAR2 (32767) := 'http://www.macalester.edu/';
BEGIN
/* request that exceptions are raised for error Status Codes */
Utl_Http.set_response_error_check (ENABLE => TRUE );
/* allow testing for exceptions like Utl_Http.Http_Server_Error */
Utl_Http.set_detailed_excp_support (ENABLE => TRUE );
/*
Utl_Http.set_proxy (
proxy => 'www-proxy.us.oracle.com',
no_proxy_domains => 'us.oracle.com'
);
*/
req := Utl_Http.begin_request (url => v_url, method => 'GET');
/*
Alternatively use method => 'POST' and Utl_Http.Write_Text to
build an arbitrarily long message
*/
/*
Utl_Http.set_authentication (
r => req,
username => 'SomeUser',
PASSWORD => 'SomePassword',
scheme => 'Basic',
for_proxy => FALSE --this info is for the target Web server
);
*/
Utl_Http.set_header (r => req, NAME => 'User-Agent', VALUE => 'Mozilla/4.0');
resp := Utl_Http.get_response (r => req);
/*
DBMS_OUTPUT.put_line ('Status code: ' || resp.status_code);
DBMS_OUTPUT.put_line ('Reason phrase: ' || resp.reason_phrase);
FOR i IN 1 .. Utl_Http.get_header_count (r => resp)
LOOP
Utl_Http.get_header (r => resp, n => i, NAME => NAME, VALUE => VALUE);
DBMS_OUTPUT.put_line (NAME || ': ' || VALUE);
END LOOP;
*/
--test
BEGIN
LOOP
Utl_Http.read_text (r => resp, DATA => v_msg);
--DBMS_OUTPUT.put_line (v_msg);
v_ans := v_ans || v_msg;
url_resp := url_resp || v_msg;
END LOOP;
EXCEPTION
WHEN Utl_Http.end_of_body
THEN
NULL;
END;
--test
Utl_Http.end_response (r => resp);
--url_resp := v_ans;
EXCEPTION
/*
The exception handling illustrates the use of "pragma-ed" exceptions
like Utl_Http.Http_Client_Error. In a realistic example, the program
would use these when it coded explicit recovery actions.
Request_Failed is raised for all exceptions after calling
Utl_Http.Set_Detailed_Excp_Support ( ENABLE=>FALSE )
And it is NEVER raised after calling with ENABLE=>TRUE
*/
WHEN Utl_Http.request_failed
THEN
DBMS_OUTPUT.put_line (
'Request_Failed: ' || Utl_Http.get_detailed_sqlerrm
);
url_resp :='Request_Failed: ' || Utl_Http.get_detailed_sqlerrm;
/* raised by URL http://xxx.oracle.com/ */
WHEN Utl_Http.http_server_error
THEN
DBMS_OUTPUT.put_line (
'Http_Server_Error: ' || Utl_Http.get_detailed_sqlerrm
);
url_resp := 'Http_Server_Error: ' || Utl_Http.get_detailed_sqlerrm;
/* raised by URL http://otn.oracle.com/xxx */
WHEN Utl_Http.http_client_error
THEN
DBMS_OUTPUT.put_line (
'Http_Client_Error: ' || Utl_Http.get_detailed_sqlerrm
);
url_resp := 'Http_Client_Error: ' || Utl_Http.get_detailed_sqlerrm;
/* code for all the other defined exceptions you can recover from */
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
url_resp := SQLERRM;
END;
Then to test it
begin
macp_url_get(url_resp => :url_resp,
'http://maps.googleapis.com/maps/api/geocode/json?address=55105&sensor=false');
end;
(I know that the googleapi will allow xml response, but there are other web APIs that I use regularly that default to JSON)
I have started using this library, and it seems promising:
https://github.com/pljson/pljson
Easy to install, and the examples are good.
To use the library in your example, add these variables to your procedure..
mapData json;
results json_list;
status json_value;
firstResult json;
geometry json;
....
Then you can manipulate the response as a json object.
-- convert the result from the get to a json object, and show some results.
mapData := json(v_ans);
-- Show the status of the request
status := mapData.get('status');
dbms_output.put_line('Status = ' || status.get_string());
IF (status.get_string() = 'OK') THEN
results := json_list(mapData.get('results'));
-- Grab the first item in the list
resultObject := json(results.head);
-- Show the human readable address
dbms_output.put_line('Address = ' || resultObject.get('formatted_address').to_char() );
-- Show the json location data
dbms_output.put_line('Location = ' || resultObject.get('geometry').to_char() );
END IF;
Running this code will output this to the dbms output:
Status = OK
Address = "St Paul, MN 55105, USA"
Location = {
"bounds" : {
"northeast" : {
"lat" : 44.9483849,
"lng" : -93.1261959
},
"southwest" : {
"lat" : 44.9223829,
"lng" : -93.200307
}
},
"location" : {
"lat" : 44.9330076,
"lng" : -93.16290629999999
},
"location_type" : "APPROXIMATE",
"viewport" : {
"northeast" : {
"lat" : 44.9483849,
"lng" : -93.1261959
},
"southwest" : {
"lat" : 44.9223829,
"lng" : -93.200307
}
}
}
It should be noted that as of Oracle 12c there is some native support of JSON. However i don't think in the current form it's as useful as the like of PLJSON included in another answer.
To use the feature you create a table with a BLOB, CLOB or Varchar2 field and add a constraint against it "column IS JSON". This enforces JSON syntax checking on that column.
As long as the "IS JSON" constraint is in place you can access the JSON values within using dot notation from SQL. To me, it doesn't seem to provide as powerful manipulation as PLJSON. You can also create an XMLType and then convert to JSON.
Useful links:
Oracle docs
Good tutorial and examples
Tutorial including XML to JSON
I wrote this library : http://reseau.erasme.org/pl-sql-library-for-JSON?lang=en, and this works great to get some json response into a plsql table.
If you only want to extract Oracle data and transform it in Json, this library is a bit "Heavy to use"...
I can propose you another code doing it better and faster :
create or replace package jsonfly as
procedure open_object(k varchar2 default null);
procedure close_object;
procedure open_array (k varchar2 default null);
procedure close_array;
procedure separation;
procedure member(k varchar2, v varchar2);
procedure member(k varchar2, n number);
procedure send;
end;
/
create or replace package body jsonfly as
--------------------------------------------------------------------------------
-- package pour générer du JSON, envoyé à la volé
--------------------------------------------------------------------------------
type tCache is table of varchar2(2000) index by binary_integer;
g_openBrace constant varchar2(2) := '{ ';
g_closeBrace constant varchar2(2) := ' }';
g_openBracket constant varchar2(2) := '[ ';
g_closeBracket constant varchar2(2) := ' ]';
g_stringDelimiter constant varchar2(1) := '"';
g_Affectation constant varchar2(3) := ' : ';
g_separation constant varchar2(3) := ', ';
g_CR constant varchar2(1) := Chr(10); -- used to indent the JSON object correctly
g_spc constant varchar2(2) := ' '; -- used to indent the JSON object correctly
g_js_comment_open constant varchar2(20) := '/*-secure-\n'; -- used to prevent from javascript hijacking
g_js_comment_close constant varchar2(20) := '\n*/'; -- used to prevent from javascript hijacking
--isObjectOpened boolean := false;
--isArrayOpened boolean := false;
t tCache;
i number := 1;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure p(s varchar2) is
begin
t(i) := s;
i := i + 1;
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
function encap (s varchar2) return varchar2 is
begin
return g_stringdelimiter || s || g_stringdelimiter;
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
function encode_string(p_string varchar2) return varchar2 is
begin
return replace(replace(replace(replace(replace(replace(replace(replace(p_string,
'\', '\\'),
'"', '\"'),
'/', '\/'),
chr(8), '\b'),
chr(9), '\t'),
chr(10), '\n'),
chr(12), '\f'),
chr(13), '\r');
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure open_object(k varchar2 default null) is
begin
if ( k is null ) then
p(g_openbrace);
else
p( encap(k) || g_affectation || g_openbrace);
end if;
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure close_object is
begin
if (t(i-1) = g_separation) then
i := i - 1;
end if;
p(g_closebrace);
separation();
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure open_array (k varchar2 default null) is
begin
if ( k is null ) then
p(g_openbracket);
else
p( encap(k) || g_affectation || g_openbracket);
end if;
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure close_array is
begin
if (t(i-1) = g_separation) then
i := i - 1;
end if;
p(g_closebracket);
separation();
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure separation is
begin
p(g_separation);
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure key(k varchar2) is
begin
p( encap(k) || g_affectation);
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure value(v varchar2) is
begin
p(v);
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure member(k varchar2, v varchar2) is
begin
p( encap(k) || g_affectation || encap(encode_string(v)));
p(g_separation);
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure member(k varchar2, n number) is
begin
p( encap(k) || g_affectation || n );
p(g_separation);
end;
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
procedure send is
begin
if (t(i-1) = g_separation) then
t.delete(i-1);
end if;
i := t.first;
while (i is not null) loop
htp.p(t(i));
i := t.next(i);
end loop;
end;
end jsonfly;
/
Oracle 12c now have native JSON support:
Oracle Database supports JavaScript Object Notation (JSON) data natively with relational database features, including transactions, indexing, declarative querying, and views
JSON data and XML data can be used in Oracle Database in similar ways. Unlike relational data, both can be stored, indexed, and queried without any need for a schema that defines the data. Oracle Database supports JSON natively with relational database features, including transactions, indexing, declarative querying, and views.
JSON data has often been stored in NoSQL databases such as Oracle NoSQL Database and Oracle Berkeley DB. These allow for storage and retrieval of data that is not based on any schema, but they do not offer the rigorous consistency models of relational databases.
To compensate for this shortcoming, a relational database is sometimes used in parallel with a NoSQL database. Applications using JSON data stored in the NoSQL database must then ensure data integrity themselves.
Native support for JSON by Oracle Database obviates such workarounds. It provides all of the benefits of relational database features for use with JSON, including transactions, indexing, declarative querying, and views.
Oracle Database queries are declarative. You can join JSON data with relational data. And you can project JSON data relationally, making it available for relational processes and tools. You can also query, from within the database, JSON data that is stored outside the database in an external table.
You can access JSON data stored in the database the same way you access other database data, including using OCI, .NET, and JDBC.
Unlike XML data, which is stored using SQL data type XMLType, JSON data is stored in Oracle Database using SQL data types VARCHAR2, CLOB, and BLOB. Oracle recommends that you always use an is_json check constraint to ensure that column values are valid JSON instances
Oracle APEX 5.0 has support for JSON using APEX_JSON package. I haven't used it but it looks interesting and I have asked my team to explore it. Our use case is to be able to pass JSON data as input parameter to stored procedure from nodejs application.
Life is happy try this:
CLOB or 20000000 characters JSON
with data as
( select
xmlelement(e,regexp_replace('{"name":"'||colname||'"}', '[[:cntrl:]]', ''),',') col1
from tblname
)
select
rtrim(replace(replace(replace(xmlagg(col1).getclobval(),'&'||'quot;','"'),'<E>',''),'</E>',''),',')
as very_long_json
from data;