Existe-t-il un moyen simple de travailler avec JSON dans Oracle? J'ai souvent recours à une procédure standard pour appeler des services Web. JSON est un format que je connais bien dans le contexte du développement Web, mais quel est le meilleur moyen de travailler avec json dans une procédure stockée? Par exemple, prenez la réponse CLOB de l'URI, convertissez-la en un objet JSON et obtenez-en une valeur?
Pour référence, voici la procédure que j'ai utilisée pour récupérer les URL
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;
Puis pour le tester
begin
macp_url_get(url_resp => :url_resp,
'http://maps.googleapis.com/maps/api/geocode/json?address=55105&sensor=false');
end;
(Je sais que le googleapi autorisera une réponse xml, mais il y a d'autres API Web que j'utilise régulièrement et qui utilisent JSON par défaut)
J'ai commencé à utiliser cette bibliothèque et cela semble prometteur: https://github.com/pljson/pljson
Facile à installer, et les exemples sont bons.
Pour utiliser la bibliothèque dans votre exemple, ajoutez ces variables à votre procédure.
mapData json;
results json_list;
status json_value;
firstResult json;
geometry json;
....
Ensuite, vous pouvez manipuler la réponse en tant qu’objet JSON.
-- 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;
L'exécution de ce code générera ceci dans la sortie dbms:
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
}
}
}
Il convient de noter qu’à partir d’Oracle 12c, il existe une prise en charge native de JSON. Cependant, je ne pense pas que ce soit aussi utile sous la forme actuelle que le type de PLJSON inclus dans une autre réponse.
Pour utiliser la fonctionnalité, créez une table avec un champ BLOB, CLOB ou Varchar2 et ajoutez une contrainte sur celle-ci "column IS JSON". Cela applique la vérification de la syntaxe JSON sur cette colonne.
Tant que la contrainte "IS JSON" est en place, vous pouvez accéder aux valeurs JSON avec la notation SQL à partir de points. Pour moi, cela ne semble pas fournir une manipulation aussi puissante que PLJSON. Vous pouvez également créer un XMLType, puis convertir en JSON.
Liens utiles:
Documents Oracle
Bon tutoriel et exemples
Didacticiel incluant XML vers JSON
J'ai écrit cette bibliothèque: http://reseau.erasme.org/pl-sql-library-for-JSON?lang=en , et cela fonctionne bien pour obtenir une réponse JSON dans une table plsql.
Si vous voulez seulement extraire des données Oracle et les transformer en Json, cette bibliothèque est un peu "lourde à utiliser" ... Je peux vous proposer un autre code qui le fera mieux et plus rapidement:
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 prend désormais en charge le JSON natif :
La base de données Oracle prend en charge les données JSON (JavaScript Object Notation) de manière native avec les fonctionnalités de base de données relationnelle, notamment les transactions, l'indexation, les requêtes déclaratives et les vues.
Les données JSON et XML peuvent être utilisées de la même manière dans Oracle Database. Contrairement aux données relationnelles, les deux peuvent être stockés, indexés et interrogés sans avoir besoin d'un schéma définissant les données. Oracle Database prend en charge JSON de manière native avec des fonctionnalités de base de données relationnelles, y compris les transactions, l'indexation, les requêtes déclaratives et les vues.
Les données JSON ont souvent été stockées dans des bases de données NoSQL telles que Oracle NoSQL Database et Oracle Berkeley DB. Ceux-ci permettent le stockage et la récupération de données qui ne sont basées sur aucun schéma, mais ils n'offrent pas les modèles de cohérence rigoureux des bases de données relationnelles.
Pour compenser cette lacune, une base de données relationnelle est parfois utilisée en parallèle avec une base de données NoSQL. Les applications utilisant des données JSON stockées dans la base de données NoSQL doivent ensuite assurer elles-mêmes l'intégrité des données.
La prise en charge native de JSON par Oracle Database évite ces solutions de contournement. Il offre tous les avantages des fonctionnalités de base de données relationnelle à utiliser avec JSON, notamment les transactions, l'indexation, les requêtes déclaratives et les vues.
Les requêtes de base de données Oracle sont déclaratives. Vous pouvez joindre des données JSON avec des données relationnelles. Et vous pouvez projeter les données JSON de manière relationnelle, en les rendant disponibles pour les processus et les outils relationnels. Vous pouvez également interroger, dans la base de données, les données JSON stockées en dehors de la base de données dans une table externe.
Vous pouvez accéder aux données JSON stockées dans la base de données de la même manière que vous accédez à d'autres données de la base de données, y compris à l'aide d'OCI, de .NET et de JDBC.
Contrairement aux données XML, stockées à l'aide du type de données SQL XMLType, les données JSON sont stockées dans la base de données Oracle à l'aide des types de données SQL VARCHAR2, CLOB et BLOB. Oracle vous recommande de toujours utiliser une contrainte de vérification is_json pour vous assurer que les valeurs de colonne sont des instances JSON valides.
Oracle APEX 5.0 prend en charge JSON avec APEX_JSON package. Je ne l'ai pas utilisé mais ça a l'air intéressant et j'ai demandé à mon équipe de l'explorer. Notre cas d'utilisation consiste à pouvoir transmettre des données JSON en tant que paramètre d'entrée à une procédure stockée à partir de l'application nodejs.
La vie est heureuse, essayez ceci:
JOB CLOB ou 20000000 caractères
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;