2011-11-02 9 views
19

C'è un modo semplice per lavorare con JSON all'interno di Oracle? Ho una procedura standard che uso spesso per chiamare i servizi web, JSON è un formato che conosco nel contesto dello sviluppo web, ma qual è il modo migliore per lavorare con json all'interno di una stored procedure? Ad esempio, prendi la risposta CLOB dall'URI, convertila in un oggetto JSON e ottieni un valore da questo?lavoro con JSON in Oracle

Nell'interesse riferimento, qui è la procedura che ho usato per recuperare 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; 

Poi per provarlo

begin 
    macp_url_get(url_resp => :url_resp, 
       'http://maps.googleapis.com/maps/api/geocode/json?address=55105&sensor=false'); 
end; 

(so che la googleapi consentirà risposta XML, ma ci sono altri API Web che uso regolarmente per impostazione predefinita su JSON)

+1

Non penso che si possa fare questo fuori dalla scatola, ma ci sono alcune persone là fuori che hanno creato delle API per questo [ http://reseau.erasme.org/pl-sql-library-for-JSON?lang=en] (libreria pl/sql per JSON - potresti dare un'occhiata al test per un esempio, sembra piuttosto semplice) e [ sourceforge.net/projects/pljson/] (pljson - questo sembra più complicato ma dovrebbe anche funzionare) – Harrison

risposta

18

Ho iniziato a utilizzare questa libreria e sembra promettente: https://github.com/pljson/pljson

Facile da installare e gli esempi sono buoni.

utilizzare la libreria nel tuo esempio, aggiungere queste variabili al procedimento ..

mapData  json; 
results  json_list; 
status  json_value; 
firstResult json; 
geometry json; 

....

Quindi è possibile manipolare la risposta come un oggetto 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'esecuzione di questo codice di uscita volontà presente all'uscita 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 
    } 
    } 
} 
+1

Va notato che questa libreria è stata spostata su GitHub https://github.com/pljson/pljson – Damian

2

Ho scritto questa libreria: http://reseau.erasme.org/pl-sql-library-for-JSON?lang=en e questo funziona perfettamente per ottenere una risposta JSON in una tabella plsql.

Se si desidera solo per estrarre i dati di Oracle e trasformarlo in JSON, questa libreria è un po ' "Heavy usare" ... posso proporre un altro codice a farlo meglio e più velocemente:

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

Si segnala che a partire dal Oracle 12c c'è qualche supporto nativo di JSON. Comunque non credo che nella forma attuale sia utile come il solito di PLJSON incluso in un'altra risposta.

Per utilizzare la funzione di creare una tabella con un BLOB, CLOB o campo Varchar2 e aggiungere un vincolo contro di essa "colonna è JSON". Questo impone il controllo della sintassi JSON su quella colonna.

Fintantoché il vincolo "IS JSON" è a posto, è possibile accedere ai valori JSON utilizzando la notazione dot di SQL. Per me, non sembra fornire una potente manipolazione come PLJSON. È inoltre possibile creare un XMLType e quindi convertirlo in JSON.

Link utili:
Oracle docs
Good tutorial and examples
Tutorial including XML to JSON

1

Oracle APEX 5.0 ha il supporto per JSON utilizzando APEX_JSON pacchetto. Non l'ho usato ma sembra interessante e ho chiesto al mio team di esplorarlo.Il nostro caso d'uso deve essere in grado di passare i dati JSON come parametro di input alla stored procedure dall'applicazione nodejs.

+0

Non era diritta avanti per installare il pacchetto APEX_JSON sul nostro database esistente senza installare il server APEX 5.0. Abbiamo deciso di utilizzare la libreria pljson (https://github.com/pljson/pljson). – Parvez

1

Oracle 12c hanno ora native JSON support:

Oracle Database supporta i dati JavaScript Object Notation (JSON) in modo nativo con funzionalità di database relazionali, ivi incluse le operazioni, l'indicizzazione, l'esecuzione di query dichiarativo, e viste

dati JSON e dati XML può essere utilizzato nel database Oracle in modi simili. A differenza dei dati relazionali, entrambi possono essere archiviati, indicizzati e interrogati senza che sia necessario uno schema che definisca i dati. Oracle Database supporta in modo nativo JSON con funzionalità di database relazionali, incluse transazioni, indicizzazione, query dichiarative e viste.

I dati JSON sono stati spesso archiviati in database NoSQL come Oracle NoSQL Database e Oracle Berkeley DB. Questi consentono la memorizzazione e il recupero di dati che non sono basati su alcuno schema, ma non offrono i rigorosi modelli di coerenza dei database relazionali.

Per compensare questa mancanza, a volte viene utilizzato un database relazionale in parallelo con un database NoSQL. Le applicazioni che utilizzano i dati JSON memorizzati nel database NoSQL devono quindi garantire l'integrità dei dati stessi.

Il supporto nativo per JSON da Oracle Database ovvia a tali soluzioni. Fornisce tutti i vantaggi delle funzionalità di database relazionali da utilizzare con JSON, incluse transazioni, indicizzazione, query dichiarative e viste.

Le query del database Oracle sono dichiarative. È possibile unire i dati JSON con i dati relazionali. E puoi proiettare i dati JSON in relazione, rendendoli disponibili per processi e strumenti relazionali. È inoltre possibile interrogare, all'interno del database, i dati JSON memorizzati al di fuori del database in una tabella esterna.

È possibile accedere ai dati JSON memorizzati nel database nello stesso modo in cui si accede ad altri dati del database, compreso l'utilizzo di OCI, .NET e JDBC.

A differenza dei dati XML, che vengono archiviati utilizzando il tipo di dati SQL XMLType, i dati JSON vengono archiviati nel database Oracle utilizzando i tipi di dati SQL VARCHAR2, CLOB e BLOB. Oracle consiglia di utilizzare sempre un vincolo di controllo is_json per garantire che i valori delle colonne siano istanze JSON valide