I am having a problem with Xero API when the API establishes the connection, in the first attempt to establish the connection, it returns an error of Unexpected character "<" while in the very 2nd attempt to establish a connection gets successful.
The Code I am using is in the form of a Function as below:
function base64encode(t in varchar2) return varchar2
is
begin
return translate(utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(t))), 'd'||CHR(10)||CHR(13), 'd');
end base64encode;
function get_access_token return varchar2
is
g_client varchar2(200);
v_secret varchar2(200);
l_result clob;
token_not_found exception;
l_token clob;
begin
g_client := mis_system_parameters.get_string_value('XERC');
v_secret := mis_system_parameters.get_string_value('XERS');
g_xero_revenue_account_code := mis_system_parameters.get_string_value('XENC');
while l_result is null or l_result like '%<%' loop
apex_web_service.g_request_headers.delete();
apex_web_service.g_request_headers(1).name := 'authorization';
apex_web_service.g_request_headers(1).value := 'Basic '|| base64encode(g_client||':'||v_secret);
apex_web_service.g_request_headers(2).name := 'Content-Type';
apex_web_service.g_request_headers(2).value := 'application/x-www-form-urlencoded';
l_result := apex_web_service.make_rest_request(
p_url => 'http://localhost/identity-xero-api-proxy/connect/token'
,p_http_method => 'POST'
,p_body => 'grant_type=client_credentials&scope=accounting.transactions accounting.contacts assets files accounting.settings'
);
end loop;
apex_json.parse(l_result);
l_token := apex_json.get_varchar2('access_token');
if apex_json.get_varchar2('access_token') is null then
raise token_not_found;
end if;
return l_token;
exception
when token_not_found then
mis_errors.ins_err
('mis_xero_customer_api.get_access_token','Token Not Found');
when others then
mis_errors.ins_err
('mis_xero_customer_api.get_access_token',l_result||' - '||SQLERRM);
raise;
end get_access_token;
Any kind of advise and help will be highly appreciated.
Thanks
I would like to ask how can I loop the value of a return in 'url_next' variable into url varible so that I can use the function until the all users imported from the api call .
so the value returned from 'url_next'
into
url := 'https://dev-9466225.okta.com/api/v1/users';
l_http_request := utl_http.begin_request(apex_string.format( url , 'GET', 'HTTP/1.1'));
create or replace function OKTA_API_X
(url IN VARCHAR2)
return varchar2
IS
v_username VARCHAR2(50);
v_email VARCHAR2(50);
v_firstname VARCHAR2(50);
v_last_name VARCHAR2(50);
l_http_request utl_http.req;
l_http_response utl_http.resp;
l_text VARCHAR2(32767);
resp CLOB := '';
l_json_obj json_object_t;
l_json_in json_array_t;
name VARCHAR2(2560);
value VARCHAR2(2024);
url_next VARCHAR2(100);
url VARCHAR2(50);
BEGIN
url := 'https://dev-9466225.okta.com/api/v1/users';
l_http_request := utl_http.begin_request(apex_string.format( url , 'GET', 'HTTP/1.1'));
utl_http.set_wallet('file:c:/okta_wallet_base/', NULL);
utl_http.set_header(l_http_request, 'content-type', 'application/json');
utl_http.set_header(l_http_request, 'authorization', 'SSWS 00mz6_ost-rfegVu5K5I6dEs33JLIFSmcBO8VxzI');
utl_http.set_header(l_http_request, 'Accept', 'application/json');
l_http_response := utl_http.get_response(l_http_request);
-- dbms_output.put_line('HTTP response status code =>' || l_http_response.status_code);
-- dbms_output.put_line('HTTP response reason phrase => ' || l_http_response.reason_phrase);
BEGIN
LOOP
utl_http.read_text(l_http_response, l_text,32766)
resp := resp || l_text;
END LOOP;
END;
--- JASON PARSING
l_json_in := json_array_t(resp);
FOR indx IN 0..l_json_in.get_size - 1 LOOP
l_json_obj := TREAT(l_json_in.get(indx) AS json_object_t);
--GET JSON OBJECT AND STRINGS FROM RESPONSE
v_firstname := l_json_obj.get_object('profile').get_string('firstName');
v_last_name := l_json_obj.get_object('profile').get_string('lastName');
v_email := l_json_obj.get_object('profile').get_string('email');
v_username := l_json_obj.get_object('profile').get_string('login');
-------------------------------
dbms_output.put_line(v_firstname);
--dbms_output.put_line(v_EMAIL);
END LOOP;
---------------------------------------------------------------------------- FOR i IN 1..
utl_http.get_header_count(l_http_response) LOOP
utl_http.get_header(l_http_response, i, name, value);
IF
name = 'link'
AND value LIKE '%rel="next"'
THEN
url_next := ( trim(TRAILING '>' FROM regexp_substr(value, 'http[^>]+>')) );
END IF;
END LOOP;
BEGIN
WHILE url_next IS NOT NULL LOOP
IF url_next <> url THEN
url := url_next;
ELSE
NULL;
END IF;
END LOOP;
END;
EXCEPTION
WHEN OTHERS THEN
BEGIN
utl_http.end_response(l_http_response);
dbms_output.put_line('ERROR →' || sqlerrm);
END;
END;
I have large text around 20000 I need convert to UTF8.
select convert('large text','UTF8') from dual
I get error:
ORA-01704: string literal too long
Try this get another error
declare
v_hugetxt varchar2(32767);
begin
v_hugetxt:='huge text'
select convert(v_hugetxt,'UTF8') into v_hugetxt
from dual;
end;
I get error
ORA-01460: unimplemented or unreasonable conversion requested tips ·
Incompatible character sets can cause an ORA-01460
I found solution but worked
FYI
create or replace function v_blobtoclob(v_blob_in in blob) return clob is
v_file_clob clob;
v_file_size integer := dbms_lob.lobmaxsize;
v_dest_offset integer := 1;
v_src_offset integer := 1;
v_blob_csid number := dbms_lob.default_csid;
v_lang_context number := dbms_lob.default_lang_ctx;
v_warning integer;
v_length number;
begin
dbms_lob.createtemporary(v_file_clob, true);
dbms_lob.converttoclob(v_file_clob,
v_blob_in,
v_file_size,
v_dest_offset,
v_src_offset,
v_blob_csid,
v_lang_context,
v_warning);
return v_file_clob;
exception
when others then
dbms_output.put_line('Error found');
end;
CREATE OR REPLACE FUNCTION clob_to_blob (p_clob CLOB, p_charsetname VARCHAR2)
RETURN BLOB
AS
l_lang_ctx INTEGER := DBMS_LOB.default_lang_ctx;
l_warning INTEGER;
l_dest_offset NUMBER := 1;
l_src_offset NUMBER := 1;
l_return BLOB;
BEGIN
DBMS_LOB.createtemporary (l_return, FALSE);
DBMS_LOB.converttoblob (
l_return,
p_clob,
DBMS_LOB.lobmaxsize,
l_dest_offset,
l_src_offset,
CASE WHEN p_charsetname IS NOT NULL THEN NLS_CHARSET_ID (p_charsetname) ELSE DBMS_LOB.default_csid END,
l_lang_ctx,
l_warning);
RETURN l_return;
END;
declare
v_hugetxt clob;
v_hugetxt2 blob;
v_hugetxt3 clob;
offset int;
clobsize number;
begin
v_hugetxt:='huge text';
select bnf.clob_to_blob(v_hugetxt,'UTF8') into v_hugetxt2 from dual;
select bnf.v_blobtoclob(v_hugetxt2) into v_hugetxt3 from dual;
dbms_output.put_line('Print CLOB');
offset:=1;
loop
exit when offset > dbms_lob.getlength(v_hugetxt3);
dbms_output.put_line(dbms_lob.substr(v_hugetxt3, 255, offset));
offset := offset + 255;
end loop;
DBMS_OUTPUT.PUT_LINE('clob out = ' || v_hugetxt3);
end;
That's it
I have JSON, I want to bypass it and display all the data that is in it. But the problem is that I can't know what data will be there. I made the code, but it only looks for top-level objects. And the rest are not. I will be grateful for your help.
DECLARE
l_object json_object_t;
l_key_list json_key_list;
v_clob CLOB;
tv apex_json.t_values;
BEGIN
-- JSON_OBJECT can figure out what keys it has...
v_clob := '{"devices":{"id":"d652f632-0835-871b-a140-58701f019000","scale_id":"88348A32BD3D149FE055000000000001"},"data":{"external_id":"40023"},"data_weight":{"weight":"20322","prevWeight":"1000","prevTransaction":"1607680754361","transaction":"1607680754361","on":"false","transactionDataCount":"1","stable":"false","duration":"12","transactionMaxWeight":"2000","perimetr":"true","driverInCar":"false"}}';
apex_json.parse(tv,v_clob);
l_object := json_object_t.parse (v_clob);
l_key_list := l_object.get_keys;
FOR counter IN 1 .. l_key_list.COUNT
LOOP
DBMS_OUTPUT.put_line (
l_key_list (counter)
|| ' : '
|| apex_json.get_varchar2(p_path => l_key_list (counter), p_values => tv));
END LOOP;
END;
If you take into account only the top level objects, then this code works
DECLARE
l_object json_object_t;
l_key_list json_key_list;
v_clob CLOB;
tv apex_json.t_values;
BEGIN
-- JSON_OBJECT can figure out what keys it has...
v_clob := '{"devices":"3423","data":"okwwwe"}';
apex_json.parse(tv,v_clob);
l_object := json_object_t.parse (v_clob);
l_key_list := l_object.get_keys;
FOR counter IN 1 .. l_key_list.COUNT
LOOP
DBMS_OUTPUT.put_line (
l_key_list (counter)
|| ' : '
|| apex_json.get_varchar2(p_path => l_key_list (counter), p_values => tv));
END LOOP;
END;
RESULT :
devices : 3423
data : okwwwe
And how to deduce the data from the first example, I can not understand. These data must correspond to the hierarchy.
If you have access to the native JSON types (JSON_ELEMENT_T, JSON_ARRAY_T, etc.) you should always use them over using the APEX_JSON package ans they will have much better performance. A package similar to the one below can be used to output the full hierarchy of a JSON object or array. Feel free to tweak the package to change the format of the output.
Package Specification
CREATE OR REPLACE PACKAGE output_json
AS
c_padding_char CONSTANT VARCHAR2 (1) := '-';
c_padding_amount CONSTANT PLS_INTEGER := 1;
PROCEDURE output_scalar (p_element json_element_t,
p_padding PLS_INTEGER,
p_json_key VARCHAR2 DEFAULT NULL);
PROCEDURE output_object (p_object json_object_t, p_padding PLS_INTEGER);
PROCEDURE output_array (p_array json_array_t, p_padding PLS_INTEGER);
PROCEDURE output_element (p_element json_element_t,
p_padding PLS_INTEGER,
p_json_key VARCHAR2 DEFAULT NULL);
END;
/
Package Body
CREATE OR REPLACE PACKAGE BODY output_json
AS
PROCEDURE output_scalar (p_element json_element_t,
p_padding PLS_INTEGER,
p_json_key VARCHAR2 DEFAULT NULL)
IS
BEGIN
DBMS_OUTPUT.put_line (
LPAD (c_padding_char, p_padding, c_padding_char)
|| CASE WHEN p_json_key IS NOT NULL THEN p_json_key || ' : ' END
|| CASE
WHEN p_element.is_boolean
THEN
CASE WHEN p_element.to_boolean THEN 'TRUE' ELSE 'FALSE' END || ' (boolean)'
WHEN p_element.is_date
THEN
TO_CHAR (p_element.TO_DATE, 'YYYY-MM-DD') || ' (date)'
WHEN p_element.is_number
THEN
p_element.TO_NUMBER || ' (number)'
WHEN p_element.is_string
THEN
p_element.TO_STRING || ' (string)'
END);
END;
PROCEDURE output_object (p_object json_object_t, p_padding PLS_INTEGER)
IS
l_keys json_key_list;
l_element json_element_t;
BEGIN
l_keys := p_object.get_keys;
FOR i IN 1 .. l_keys.COUNT
LOOP
l_element := p_object.get (l_keys (i));
output_element (l_element, p_padding, l_keys (i));
END LOOP;
END;
PROCEDURE output_array (p_array json_array_t, p_padding PLS_INTEGER)
IS
BEGIN
FOR i IN 0 .. p_array.get_size - 1
LOOP
output_element (p_array.get (i), p_padding);
END LOOP;
END;
PROCEDURE output_element (p_element json_element_t,
p_padding PLS_INTEGER,
p_json_key VARCHAR2 DEFAULT NULL)
IS
BEGIN
DBMS_OUTPUT.put_line (
CASE
WHEN p_json_key IS NOT NULL AND NOT p_element.is_scalar THEN p_json_key || ' : '
END);
IF p_element.is_scalar
THEN
output_scalar (p_element, p_padding, p_json_key);
ELSIF p_element.is_object
THEN
output_object (TREAT (p_element AS json_object_t), p_padding + c_padding_amount);
ELSIF p_element.is_array
THEN
output_array (TREAT (p_element AS json_array_t), p_padding + c_padding_amount);
END IF;
END;
END;
/
Example Call
DECLARE
l_clob CLOB;
BEGIN
l_clob :=
'{"arr":[1,2,3],"devices":{"id":"d652f632-0835-871b-a140-58701f019000","scale_id":"88348A32BD3D149FE055000000000001"},"data":{"external_id":"40023"},"data_weight":{"weight":"20322","prevWeight":"1000","prevTransaction":"1607680754361","transaction":"1607680754361","on":"false","transactionDataCount":"1","stable":"false","duration":"12","transactionMaxWeight":"2000","perimetr":"true","driverInCar":"false"}}';
output_json.output_element (json_element_t.parse (l_clob), 0);
END;
/
Example Output
arr :
--1 (number)
--2 (number)
--3 (number)
devices :
--id : "d652f632-0835-871b-a140-58701f019000" (string)
--scale_id : "88348A32BD3D149FE055000000000001" (string)
data :
--external_id : "40023" (string)
data_weight :
--weight : "20322" (string)
--prevWeight : "1000" (string)
--prevTransaction : "1607680754361" (string)
--transaction : "1607680754361" (string)
--on : "false" (string)
--transactionDataCount : "1" (string)
--stable : "false" (string)
--duration : "12" (string)
--transactionMaxWeight : "2000" (string)
--perimetr : "true" (string)
--driverInCar : "false" (string)
I also made my own version, but it is limited to 2 levels of objects, and does not take into account arrays.
DECLARE
l_object json_object_t;
l_key_obj json_key_list;
v_clob CLOB;
tv apex_json.t_values;
obj_in_obj JSON_OBJECT_T;
l_key_list json_key_list;
BEGIN
v_clob := '{
"devices":{
"id":"d652f632-0835-871b-a140-58701f019000",
"scale_id":"88348A32BD3D149FE055000000000001"
},
"data":{
"external_id":"40023"
},
"data_weight":{
"weight":"20322",
"prevWeight":"1000",
"prevTransaction":"1607680754361",
"transaction":"1607680754361",
"on":"false",
"transactionDataCount":"1",
"stable":"false",
"duration":"12",
"transactionMaxWeight":"2000",
"perimetr":"true",
"driverInCar":"false"
}
}';
apex_json.parse(tv,v_clob);
l_object := json_object_t.parse (v_clob);
l_key_list := l_object.get_keys;
FOR counter IN 1 .. l_key_list.COUNT
LOOP
obj_in_obj := l_object.get_object(l_key_list (counter));
l_key_obj := obj_in_obj.get_keys;
FOR counter_all_obj IN 1 .. l_key_obj.COUNT
LOOP
DBMS_OUTPUT.put_line (
l_key_list (counter)||'.'||l_key_obj (counter_all_obj)
|| ' : '
|| apex_json.get_varchar2(p_path => l_key_list (counter)||'.'|| l_key_obj (counter_all_obj), p_values => tv));
END LOOP;
END LOOP;
END;
I made this WebService in .NET which response is a byte[] which is a base64binary. I need to receive this in ORACLE (10.2.0).
I already have the response stored in a CLOB variable, I can read the value of the response this way:
SELECT EXTRACT(XMLTYPE(l_clob),
'//GetBlobResponse/GetBlobResult/text()',
'xmlns="http://tempuri.org/"')
FROM DUAL;
This throws me something like "JVBERi0xLjMNCiX5+prnDQo1IDAgb2JqDQo8PA0KL..." and so on, all this on a XMLTYPE CLOB COLUMN.
What I want to do is something like this:
DECLARE
VAR BLOB;
BEGIN
SELECT EXTRACT(XMLTYPE(l_clob),
'//GetBlobResponse/GetBlobResult/text()',
'xmlns="http://tempuri.org/"')
INTO VAR
FROM DUAL;
END;
I tried to do "VAR CLOB" with no luck.
I don't want to insert this on a TABLE, I want to use it with another Procedure that receives a BLOB.
And for the record is a PDF file.
Well i hitted the answer,after doin this:
DECLARE
l_xml XMLTYPE;
l_clob CLOB;
x_clob CLOB;
l_blob BLOB;
BEGIN
x_clob := '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetBlobResponse xmlns="http://tempuri.org/">
<GetBlobResult>[BASE64BINARY]</GetBlobResult>
</GetBlobResponse>
</soap:Body>
</soap:Envelope>';
SELECT EXTRACT(XMLTYPE(x_clob),
'//GetBlobResponse/GetBlobResult/text()',
'xmlns="http://tempuri.org/"')
INTO l_xml
FROM DUAL;
l_clob := l_xml.getClobVal();
l_blob := F_DECODE_BASE64(l_clob);
END;
With the function doin this:
FUNCTION F_DECODE_BASE64(p_clob_in in clob) return blob is
v_blob blob;
v_result blob;
v_offset integer;
v_buffer_size binary_integer := 48;
v_buffer_varchar varchar2(48);
v_buffer_raw raw(48);
BEGIN
IF p_clob_in is null then
return null;
END IF;
dbms_lob.createtemporary(v_blob, true);
v_offset := 1;
FOR i in 1 .. ceil(dbms_lob.getlength(p_clob_in) / v_buffer_size) LOOP
dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
v_buffer_raw := utl_raw.cast_to_raw(v_buffer_varchar);
v_buffer_raw := utl_encode.base64_decode(v_buffer_raw);
dbms_lob.writeappend(v_blob, utl_raw.length(v_buffer_raw), v_buffer_raw);
v_offset := v_offset + v_buffer_size;
END LOOP;
v_result := v_blob;
dbms_lob.freetemporary(v_blob);
RETURN v_result;
END f_decode_base64;
This gave me the answer i was lookin for.