Lloyd
Lloyd

Reputation: 1403

work with json in oracle

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)

Upvotes: 23

Views: 54092

Answers (7)

ironix
ironix

Reputation: 108

Maybe this will not be answer to your question as a whole, but I will try to asnwer at least part of it.

Is there an easy way to work with JSON within oracle?

There is a function JSON_VALUE which is quite handy to extract values you need out of JSON. For example:

SELECT JSON_VALUE('{"id": "1234","name": "ABCD"}', '$.name') FROM dual;

This will return "ABCD".

Upvotes: 0

Shahbaz Ali
Shahbaz Ali

Reputation: 81

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;

Upvotes: 1

jmc
jmc

Reputation: 1058

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

Upvotes: 18

Nashev
Nashev

Reputation: 635

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

Upvotes: 2

Parvez
Parvez

Reputation: 641

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.

Upvotes: 1

Stuart Brock
Stuart Brock

Reputation: 3892

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

Upvotes: 7

Pierre-Gilles Levallois
Pierre-Gilles Levallois

Reputation: 4371

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;
/

Upvotes: 3

Related Questions