web-dev-qa-db-fra.com

travailler avec json dans Oracle

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)

21
Lloyd

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
    }
  }
}
18
jmc

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

4
Stuart Brock

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. 

2
Nashev

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.

1
Parvez

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;
0
Shahbaz Ali