Con la llegada de APEX 26.1 hace apenas dos semanas y las nuevas funcionalidades que ofrece, cobra notoriedad el conocer al menos las bases sobre una de las notaciones más importantes utilizadas en la actualidad en la Internet.
Estoy hablando de JSON, de modo que en esta ocasión quiero escribir al respecto y sembrar las bases necesarias para su utilización dentro de aplicaciones APEX.
Voy a dividir esta publicación en dos grandes temas.
Introducción a JSON
Creo que lo primero que debo definir es ¿qué es JSON?
JSON viene de Javascript Object Notation (notación de objetos Javascript) y no, no es Javascript.
De acuerdo con el sitio oficial, “JSON es un formato ligero de intercambio de datos, fácil de leer y escribir para los humanos, fácil de analizar y generar para las máquinas”.
JSON toma como fundamento un subconjunto del estándar del lenguaje de programación JavaScript ECMA-262, 3.ª edición, sin embargo, JSON es un formato de texto completamente independiente del lenguaje, pero utiliza convenciones familiares para los programadores de la familia de lenguajes C, incluidos C, C++, C#, Java, JavaScript, Perl, Python y muchos otros; lo que lo hacen un lenguaje ideal para el intercambio de datos.
JSON consta de dos estructuras básicas:
Colección de pares nombre/valor
Como se mencionó, una de las dos estructuras que conforman JSON, son los objetos, la estructura general y correcta la puedes observar en la imagen de arriba.
Un objeto JSON es una colección de pares nombre/valor que:
- se encuentran encapsulados entre corchetes {}
- está compuesto por pares nombre/valor los cuales se separan con un carácter “:”
- los pares dentro del objeto no siguen ningún orden determinado
- si existe más de un par nombre/valor dentro del objeto, estos se separan con una coma “,”
- por regla general, se dejan espacios en blando:
- luego del corchete de inicio y antes del corchete final,
- antes y después del separador “:”
- puede tener un nombre que identifica su contenido
- puede ser vacío (sin ningún par nombre/valor dentro.
- dentro de los pares, el nombre va dentro de comillas dobles (“)
|
1 2 3 4 5 6 7 8 |
{ "usuario": "Luis Quiros", "edad": 33, "activo": true, "direccion": "Avenida 1, calle 23 oeste", "ciudad": "Albuquerque", "codigo_postal": 10101 } |
El código anterior es un ejemplo de un objeto JSON, el cual está compuesto por seis pares diferentes: usuario, edad, activo, direccion, ciudad y codigo_postal; cada uno con su respectivo valor.
Como habrá observado, los valores pueden tener diferentes tipos de datos, los cuales incluyen:
- cadenas alfanuméricas (string)
- números (number)
- falso o verdadero (true/false)
- nulo (null)
- arreglo (array)
- objeto
Si, un objeto puede formar parte de otro como parte de un par nombre/valor.
La imagen a continuación ilustra los diferentes tipos de valores que pueden ser parte de un par nombre/valor.
Lista ordenada de valores
La segunda estructura fundamental de JSON, son los arreglos ordenados de valores o arreglos, arrays o como quieras referirte a una serie de valores ordenados e indexados.
Tomemos la imagen anterior como base para hacer un análisis de lo que es un arreglo de valores. De aquí en adelante, me voy a seguir refiriendo a las lista de valores ordenados como arreglo.
Entonces, un arreglo es:
- una estructura ordenada de valores que se encuentran encapsulados dentro de paréntesis cuadrados []
- los valores del arreglo pueden ser una cadena entrecomillada, un número, un valor false o verdadero, un valor nulo y también un objeto o un arreglo
- de acuerdo con lo indicado en el punto anterior, podemos anidar arreglos sin ningún problema
- los valores son separados por coma “,”
- hacemos referencia a los valores del arreglo utilizando un índice de base 1 que empieza por cero.
Entonces, si tenemos el arreglo [“manzana”,”uva”,”banano”,”naranja”,”frambuesa”], el valor 0 del arreglo corresponde a “manzana” y así respectivamente al valor 4 que corresponde a “frambuesa”.
Veamos como luciría un objeto JSON con un arreglo llamado “frutas” y que tiene los valores anteriores.
|
1 2 3 |
{ "frutas":["manzana" , "uva" , "banano" , "naranja" , "frambuesa"] } |
Generalidades
Demos respuesta a algunas preguntas sobre JSON.
1. ¿Para que sirve JSON?
JSON es útil y se utiliza para realizar intercambio de datos entre sistemas. Al ser fácilmente leído y escrito tanto por humanos como por computadoras, permite trasladar grandes cantidades de datos, los cuales son manejados de manera sencilla, ya sea por una persona o por un sistema.
2. ¿En donde se utiliza?
En la actualidad, es utilizado de manera generalizada en Internet, así como en bases de datos, de hecho existen bases de datos que manejan el almacenado de datos en formato JSON, lo que nos da flexibilidad y nos permite manejar diferentes tipos de datos o estructuras en una misma tabla. Una de las bases de datos que manejan este tipo de almacenamiento es Oracle.
Es también el formato principal en que los servicios web RESTful generan las respuestas a sus solicitudes.
3. ¿Oracle Application Express es capa de manejar nomenclatura JSON?
Si. APEX cuenta con una API específica para el manejo de JSON, que nos permite tanto generar como procesar objetos JSON y, es justo de lo que me voy a ocupar en la siguiente sección.
APEX_JSON
APEX_JSON es un paquete que en la versión 26.1 de APEX, está compuesto por 56 funciones y procedimientos que nos permiten entre otras cosas:
- Leer una cadena que contiene datos JSON
- Convertir esa cadena a un formato interno
- Utilizar rutinas get% para extraer diferentes tipos de datos
- Acceder a los datos y realizar búsquedas dentro de ellos
- Convertir cadenas JSON a formato XML
- Generar datos en formato JSON.
El paquete maneja una serie de constantes que son utilizadas al realizar el análisis de los datos. Estas constantes son:
- subtype t_kind is binary_integer range 1 .. 8;
- c_null constant t_kind := 1;
- c_true constant t_kind := 2;
- c_false constant t_kind := 3;
- c_number constant t_kind := 4;
- c_varchar2 constant t_kind := 5;
- c_object constant t_kind := 6;
- c_array constant t_kind := 7;
- c_clob constant t_kind := 8;
Los datos JSON son almacenados como registros en una tabla indexada por varchar2 y se utiliza el discriminador “kind” que aparece como un subtipo en la lista de constantes de arriba y es el encargado de determinar sin los valores son nulo, falso, verdadero, número, cadenas, un clob, un objeto a un arreglo. Entonces, depende de “kind” cuales campos de registro son usados y de que manera.
En el caso de las fechas, estas se utilizan utilizando el formato definido en la constante c_date_iso8601 de la siguiente manera:
c_date_iso8601 constant varchar2(30) := ‘yyyy-mm-dd”T”hh24:mi:ss”Z”‘;
El API APEX_JSON, nos sirve para dos tareas básicas:
Generar objetos JSON
En ocasiones vamos a necesitar trasladar una cantidad grande de datos para procesar, trasladar o compartir. APEX_JSON te ayuda a lograrlo. Si recuerdas del inicio de este artículo, un objeto JSON es un grupo de pares nombre/valor que se encuentran encasillados dentro de corchetes { y } y, que también podemos tener arreglos ordenados dentro del objeto JSON, los cuales se definen dentro de paréntesis cuadrados [ y ].
En la siguiente tabla te describo cada uno de estos elementos JSON y su correspondiente procedimiento dentro de APEX_JSON.
| Elemento JSON | Función | Procedimiento APEX_JSON |
| { | Abrir objeto | OPEN_OBJECT |
| } | Cerrar objeto | CLOSE_OBJECT |
| [ | Abrir arreglo | OPEN_ARRAY |
| ] | Cerrar arreglo | CLOSE_ARRAY |
| nombre:valor | Agregar par nombre/valor | WRITE |
Ahora veamos algunos ejemplos sencillos.
Primero, supongamos que queremos convertir a formato JSON un grupo de datos sobre una persona que incluye: nombre, edad, fecha de nacimiento, estatura y genero, los datos que vamos a incluir en este objeto respectivamente serían: “Eddie Molina, 60, 22/02/1966, 174, masculino”.
El código PL/SQL para realizar esta tarea sería:
|
1 2 3 4 5 6 7 8 9 |
Begin apex_json.open_object; apex_json.write('nombre','Eddie Molina'); apex_json.write('edad',60); apex_json.write('fecha_nacimiento',to_date('22/02/1966','DD/MM/YYYY')); apex_json.write('estatura',174); apex_json.write('genero','masculino'); apex_json.close_object; End; |
Este código, genera el siguiente objeto JSON:
|
1 2 3 4 5 6 7 |
{ "nombre":"Eddie Molina", "edad":60, "fecha_nacimiento":"1966-02-22T00:00:00Z", "estatura":174, "genero":"masculino" } |
Si desea probar este código y comprobar que deberas funciona, simplemente cópielo y ejecútelo en una sesión de SQL, ya sea dentro de APEX, o utilizando SQLcl o SQL Developer, lo único que debe hacer para corroborar la salida, es mostrar el resultado, para lo cual, lo punico que debe hacer es agregar antes del End , la siguiente línea:
|
1 |
dbms_output.put_line(apex_json.get_clob_output); |
La única diferencia es que el resultado estará en una sola línea y no se mostrará indentado como en el ejemplo de salida.
El procedimiento apex_json.get_clob_output lo que hace es retornar el contenido del clob temporal en el que se va armando el objeto JSON que queremos generar.
Ahora compliquemos un poco el ejemplo y supongamos que queremos agregar los datos anteriores dentro de un objeto que se va a llamar “persona” y en el que además vamos a agregar un objeto anidado con nombre “direccion” y que contiene los pares provincia, cantón, distrito y detalle, que van a contener de manera respectiva Alajuela, Grecia, Puente de Piedra, Calle Lomas #25.
En este caso el código se convertirá en:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Begin apex_json.open_object; apex_json.open_object('persona'); apex_json.write('nombre','Eddie Molina'); apex_json.write('edad',60); apex_json.write('fecha_nacimiento',to_date('22/02/1966','DD/MM/YYYY')); apex_json.write('estatura',174); apex_json.write('genero','masculino'); apex_json.open_object('direccion'); apex_json.write('provincia','Alajuela'); apex_json.write('canton','Grecia'); apex_json.write('distrito','Puente de Piedra'); apex_json.write('detalle','Calle Lomas #25'); apex_json.close_object; apex_json.close_object; apex_json.close_object; End; |
El objeto JSON resultante y formateado, luce así:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "persona":{ "nombre":"Eddie Molina", "edad":60, "fecha_nacimiento":"1966-02-22T00:00:00Z", "estatura":174, "genero":"masculino", "direccion":{ "provincia":"Alajuela", "canton":"Grecia", "distrito":"Puente de Piedra", "detalle":"Calle Lomas #25" } } } |
Como puede ver, ahora tenemos el objeto JSON, el cual, esta compuesto por un segundo objeto “persona”, que incluye los datos del primer ejemplo, pero que además, incluye un nuevo par con el nombre “direccion”, que a su vez es un objeto que contiene cuatro pares nombre/valor con la información de la dirección de la persona.
Finalmente, veamos un ejemplo en el que además agregamos un arreglo con nombre “habilidades”, el cual contiene las habiliades técnicas de la persona, cuyos valores van a incluir “PL/SQL, SQL, APEX, ORDS, HTML, CSS, Javascript, Oracle 12c, Oracle 19c, Oracle 26ai”.
El código PL/SQL es:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Begin apex_json.open_object; apex_json.open_object('persona'); apex_json.write('nombre','Eddie Molina'); apex_json.write('edad',60); apex_json.write('fecha_nacimiento',to_date('22/02/1966','DD/MM/YYYY')); apex_json.write('estatura',174); apex_json.write('genero','masculino'); apex_json.open_object('direccion'); apex_json.write('provincia','Alajuela'); apex_json.write('canton','Grecia'); apex_json.write('distrito','Puente de Piedra'); apex_json.write('detalle','Calle Lomas #25'); apex_json.close_object; apex_json.open_array('habilidades'); apex_json.write('PL/SQL'); apex_json.write('SQL'); apex_json.write('APEX'); apex_json.write('ORDS'); apex_json.write('HTML'); apex_json.write('CSS'); apex_json.write('Javascript'); apex_json.write('Oracle 12c'); apex_json.write('Oracle 19c'); apex_json.write('Oracle 26ai'); apex_json.close_array; apex_json.close_object; apex_json.close_object; End; |
Y el objeto JSON resultante:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
{ "persona":{ "nombre":"Eddie Molina", "edad":60, "fecha_nacimiento":"1966-02-22T00:00:00Z", "estatura":174, "genero":"masculino", "direccion":{ "provincia":"Alajuela", "canton":"Grecia", "distrito":"Puente de Piedra", "detalle":"Calle Lomas #25" }, "habilidades":[ "PL\/SQL", "SQL", "APEX", "ORDS", "HTML", "CSS", "Javascript", "Oracle 12c", "Oracle 19c", "Oracle 26ai" ] } } |
Analizar y procesar objetos JSON
Como ya mencioné con anterioridad, la otra gran utilidad que nos proporciona el API APEX_JSON, es la posibilidad de analizar y procesar el contenido de un objeto JSON.
Para lo que resta de esta subsección, vamos a utilizar el siguiente objeto JSON:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "department":{ "department_number":10, "department_name":"ACCOUNTING", "employees":[ { "employee_number":7782, "employee_name":"CLARK" }, { "employee_number":7839, "employee_name":"KING" }, { "employee_number":7934, "employee_name":"MILLER" } ] }, "metadata":{ "published_date":"10-JUN-2024", "publisher":"expertos-oracle-apex.com" } } |
En el ejemplo anterior, que nos servirá para ejemplificar el análisis de un objeto JSON, tenemos un objeto “department”, que contiene la información correspondiente al departamento 10 de la tabla dept e incluye los pares department_number, department_name y employees que es un arreglo que contiene para cada empleado del departamento los pares employee_number y employee_name.
Además, tiene un segundo objeto “metadata” que contiene los pares published_date y publisher.
Para analizar y procesar un objeto JSON, contamos con las siguientes funciones y procedimientos:
| Función/Procedimiento | Utilidad |
| APEX_JSON.PARSE | Analiza un objeto de tipo VARCHAR2 o CLOB con formato JSON y coloca sus valores en un objeto temporal. |
| APEX_JSON.DOES_EXIST | Determina si una ruta dada apunta a un valor existente y devuelve un valor verdadero o falso, dependiendo de si el valor existe o no. |
| APEX_JSON.FIND_PATHS_LIKE | Esta función retorna una tabla con las rutas que coinciden con una cadena de búsqueda específica. |
| APEX_JSON.GET_MEMBERS | Obtiene y retorna el nombre de los miembros (pares) de un objeto dado. |
| APEX_JSON.GET_COUNT | Retorna el número de miembros de un arreglo o miembros del objeto. |
| APEX_JSON.GET_NUMBER | Retorna el valor de un par cuyo valor es numérico. |
| APEX_JSON.GET_VARCHAR2 | Retorna el valor de un par cuyo contenido de una cadena VARCHAR2. |
| APEX_JSON.GET_DATE | Retorna el valor de un par cuyo contenido es una fecha. |
| APEX_JSON.GET_BOOLEAN | Retorna el valor de un par cuyo contenido es de tipo BOOLEAN. |
| APEX_JSON.GET_CLOB | Retorna el valor de un par cuyo contenido es un CLOB. |
Además de las funciones y procedimientos de la tabla anterior existen algunas más, pero para introducir el tema y explicar como procesar y analizar un objeto JSON, las anteriores son más que suficientes.
De acuerdo con la tabla anterior, entonces si tenemos un objeto JSON, vamos a poder buscar dentro de este si existe un valor o ruta determinada, contar los elementos dentro de un arreglo, extraer los nombres de los miembros o sus valores para poder utilizarlos en un procedimiento PL/SQL y hacer con ellos lo que requerimos.
De donde obtenemos el objeto JSON es indiferente, puede:
- estar almacenado en una tabla de nuestra base de datos,
- ser generado a partir de un select sobe una tabla normal de base de datos (este es otro tema),
- ser la respuesta devuelta por un servicio web REST al cual llamamos,
- ser un archivo físico.
Lo anterior no importa, una vez tengamos el objeto, podemos procesarlo y analizarlo.
Empecemos por lo básico. Hagamos un análisis del objeto y luego obtenemos los datos básicos del departamento (id y nombre), para eso vamos a usar el siguiente fragmento de código:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
DECLARE l_json_text VARCHAR2(32767); l_count PLS_INTEGER; l_members WWV_FLOW_T_VARCHAR2; l_paths APEX_T_VARCHAR2; l_exists BOOLEAN; BEGIN l_json_text := '{ "department": { "department_number": 10, "department_name": "ACCOUNTING", "employees": [ { "employee_number": 7782, "employee_name": "CLARK" }, { "employee_number": 7839, "employee_name": "KING" }, { "employee_number": 7934, "employee_name": "MILLER" } ] }, "metadata": { "published_date": "10-JUN-2024", "publisher": "expertos-oracle-apex.com" } }'; apex_json.parse(l_json_text); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Departamento (Búsqueda Ruta Básica)'); DBMS_OUTPUT.put_line('Número de Departamento : ' || APEX_JSON.get_number(p_path => 'department.department_number')); DBMS_OUTPUT.put_line('Nombre de Departamento : ' || APEX_JSON.get_varchar2(p_path => 'department.department_name')); END; |
En el fragmento anterior:
- lo primero que hacemos es definir nuestro objeto JSON que agregué al principio de esta sección en la variable l_json_text
- Luego procedemos a realizar el análisis del objeto, utilizando el procedimiento apex_json.parse, al que le vamos a pasar el objeto JSON que definimos en l_json_text
- finalmente, empezamos a extraer información del objeto JSON analizado:
- extraemos el id o número de departamento que es un número y para esto utilizamos la función apex_json.get_number
- luego estraemos el nombre del departamento, que lógicamente es una cadena de caracteres, por lo que utilizamos la función apex_json.get_varchar2
Al ejecutar el fragmento de código vamos a obtener algo como :
Si es observador del código, habrá notado que las funciones get utilizan un parámetro p_path que recibe una ruta, en este caso “department.department_number” o “department.department_name”. Si recuerda o consulta el objeto JSON de ejemplo que estamos usando, este objeto tiene dentro un objeto llamado “department”, que tiene tres miembros (pares nombre/valor): department_number, department_name y employees, las rutas se arman siguiendo los nombres de objetos/miembros separados por puntos, de modo que la ruta al número de departamento es “department.department_number”, la ruta al nombre del departamento “department.department_name” y la ruta a employees “department.employees[%d]”.
Si observa bien, la ruta a employees cambia un poco, pues agrega “[%d]” al final de la ruta, esto es porque el miembro employees es un arreglo que puede tener “n” valores y “[%d]” es un parámetro que maneja el índice al valor dentro del arreglo. Esto es algo que puede quedar más claro con el siguiente ejemplo.
Modifiquemos el fragmento y agreguemos unas cuantas líneas más para obtener el número de empleados dentro del departamento y luego por medio de un bucle, mostrando número y nombre de empleados.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
DECLARE l_json_text VARCHAR2(32767); l_count PLS_INTEGER; l_members WWV_FLOW_T_VARCHAR2; l_paths APEX_T_VARCHAR2; l_exists BOOLEAN; BEGIN l_json_text := '{ "department": { "department_number": 10, "department_name": "ACCOUNTING", "employees": [ { "employee_number": 7782, "employee_name": "CLARK" }, { "employee_number": 7839, "employee_name": "KING" }, { "employee_number": 7934, "employee_name": "MILLER" } ] }, "metadata": { "published_date": "10-JUN-2024", "publisher": "expertos-oracle-apex.com" } }'; apex_json.parse(l_json_text); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Departamento (Búsqueda Ruta Básica)'); DBMS_OUTPUT.put_line('Número de Departamento : ' || APEX_JSON.get_number(p_path => 'department.department_number')); DBMS_OUTPUT.put_line('Nombre de Departamento : ' || APEX_JSON.get_varchar2(p_path => 'department.department_name')); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Empleados (Ciclo por el arreglo)'); l_count := APEX_JSON.get_count(p_path => 'department.employees'); DBMS_OUTPUT.put_line('Número de Empleados : ' || l_count); FOR i IN 1 .. l_count LOOP DBMS_OUTPUT.put_line('Indice de empleado : ' || i); DBMS_OUTPUT.put_line('Número de empleado : ' || APEX_JSON.get_number(p_path => 'department.employees[%d].employee_number', p0 => i)); DBMS_OUTPUT.put_line('Nombre de empleado : ' || APEX_JSON.get_varchar2(p_path => 'department.employees[%d].employee_name', p0 => i)); END LOOP; END; |
En este caso, hemos agregado las funciones apex_json.get_count para obtener el número de empleados en el departamento, el cual luego vamos a utilizar dentro del bucle “for”.
Adicionalmente, hemos agregado un segundo parámetro “p0” a las funciones apex_json.get_number y apex_json.get_varchar2, además de que la ruta tiene un nuevo miembro “employees[%d]”, el segundo parámetro toma el valor de la variable de control del ciclo “i” para de esta manera obtener los datos del empleado correspondiente dentro del arreglo.
Vamos a extender un poco el fragmento, para utilizar la función apex_json.get_members para obtener el número de miembros del objeto “department”. esta función devuelve un tipo especial de de datos “WWV_FLOW_T_VARCHAR2”, una tabla de tipo VARCHAR2 que contiene los nombres de los miembros del objeto indicado.
Veamos como luce ahora el fragmento de código.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
DECLARE l_json_text VARCHAR2(32767); l_count PLS_INTEGER; l_members WWV_FLOW_T_VARCHAR2; l_paths APEX_T_VARCHAR2; l_exists BOOLEAN; BEGIN l_json_text := '{ "department": { "department_number": 10, "department_name": "ACCOUNTING", "employees": [ { "employee_number": 7782, "employee_name": "CLARK" }, { "employee_number": 7839, "employee_name": "KING" }, { "employee_number": 7934, "employee_name": "MILLER" } ] }, "metadata": { "published_date": "10-JUN-2024", "publisher": "expertos-oracle-apex.com" } }'; apex_json.parse(l_json_text); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Departamento (Búsqueda Ruta Básica)'); DBMS_OUTPUT.put_line('Número de Departamento : ' || APEX_JSON.get_number(p_path => 'department.department_number')); DBMS_OUTPUT.put_line('Nombre de Departamento : ' || APEX_JSON.get_varchar2(p_path => 'department.department_name')); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Empleados (Ciclo por el arreglo)'); l_count := APEX_JSON.get_count(p_path => 'department.employees'); DBMS_OUTPUT.put_line('Número de Empleados : ' || l_count); FOR i IN 1 .. l_count LOOP DBMS_OUTPUT.put_line('Indice de empleado : ' || i); DBMS_OUTPUT.put_line('Número de empleado : ' || APEX_JSON.get_number(p_path => 'department.employees[%d].employee_number', p0 => i)); DBMS_OUTPUT.put_line('Nombre de empleado : ' || APEX_JSON.get_varchar2(p_path => 'department.employees[%d].employee_name', p0 => i)); END LOOP; DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Verificar elementos (miembros) dentro de ruta'); l_members := APEX_JSON.get_members(p_path=>'department'); DBMS_OUTPUT.put_line('Número de Miembros : ' || l_members.COUNT); FOR i IN 1 .. l_members.COUNT LOOP DBMS_OUTPUT.put_line('Indice de Miembro : ' || i); DBMS_OUTPUT.put_line('Nombre de Miembro : ' || l_members(i)); END LOOP; END; |
El resultado al ejecutar el fragmento de código es el que se muestra a continuación.
Como puede ver, las líneas agregadas, obtienen y muestran los nombres de los miembros del objeto “department”.
En el ejemplo siguiente, vamos a agregar las líneas para determinar si un elemento existe dentro del arreglo “employees”. Específicamente, vamos a verificar si existe dentro del miembro “employee_name” el valor “MILLER”.
Para esto vamos a utilizar la función apex_json.find_paths_like la cual recibe los parámetros:
- p_return_path en este caso la ruta al elemento del arreglo que contiene la información (si existe),
- p_subpath, el miembro dentro del arreglo en el que vamos a realizar la búsqueda,
- p_value, el valor por el que vamos a buscar.
El nuevo fragmento de código ahora es:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
DECLARE l_json_text VARCHAR2(32767); l_count PLS_INTEGER; l_members WWV_FLOW_T_VARCHAR2; l_paths APEX_T_VARCHAR2; l_exists BOOLEAN; BEGIN l_json_text := '{ "department": { "department_number": 10, "department_name": "ACCOUNTING", "employees": [ { "employee_number": 7782, "employee_name": "CLARK" }, { "employee_number": 7839, "employee_name": "KING" }, { "employee_number": 7934, "employee_name": "MILLER" } ] }, "metadata": { "published_date": "10-JUN-2024", "publisher": "expertos-oracle-apex.com" } }'; apex_json.parse(l_json_text); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Departamento (Búsqueda Ruta Básica)'); DBMS_OUTPUT.put_line('Número de Departamento : ' || APEX_JSON.get_number(p_path => 'department.department_number')); DBMS_OUTPUT.put_line('Nombre de Departamento : ' || APEX_JSON.get_varchar2(p_path => 'department.department_name')); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Empleados (Ciclo por el arreglo)'); l_count := APEX_JSON.get_count(p_path => 'department.employees'); DBMS_OUTPUT.put_line('Número de Empleados : ' || l_count); FOR i IN 1 .. l_count LOOP DBMS_OUTPUT.put_line('Indice de empleado : ' || i); DBMS_OUTPUT.put_line('Número de empleado : ' || APEX_JSON.get_number(p_path => 'department.employees[%d].employee_number', p0 => i)); DBMS_OUTPUT.put_line('Nombre de empleado : ' || APEX_JSON.get_varchar2(p_path => 'department.employees[%d].employee_name', p0 => i)); END LOOP; DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Verificar elementos (miembros) dentro de ruta'); l_members := APEX_JSON.get_members(p_path=>'department'); DBMS_OUTPUT.put_line('Número de Miembros : ' || l_members.COUNT); FOR i IN 1 .. l_members.COUNT LOOP DBMS_OUTPUT.put_line('Indice de Miembro : ' || i); DBMS_OUTPUT.put_line('Nombre de Miembro : ' || l_members(i)); END LOOP; DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Buscar por elementos coincidentes en un arreglo'); l_paths := APEX_JSON.find_paths_like( p_return_path => 'department.employees[%]', p_subpath => '.employee_name', p_value => 'MILLER' ); DBMS_OUTPUT.put_line('Rutas coincidentes : ' || l_paths.COUNT); FOR i IN 1 .. l_paths.COUNT loop DBMS_OUTPUT.put_line('Número de Empleado : ' || APEX_JSON.get_number(p_path => l_paths(i)||'.employee_number')); DBMS_OUTPUT.put_line('Nombre de Empleado : ' || APEX_JSON.get_varchar2(p_path => l_paths(i)||'.employee_name')); END LOOP; END; |
Si ejecutamos, el resultado es:
Finalmente, vamos a agregar un nuevo fragmento a nuestro código, para verificar si una ruta existe, para lo cual vamos a utilizar la función apex_json.does_exist, para verificar si dentro del arreglo “employees” existe una entrada con el índice 4. Esta función recibe como parámetros:
- p_path, la ruta que vamos a verificar
- p0, el número del índice que vamos a buscar
A diferencia del ejemplo anterior, en logar de buscar un valor, lo que hacemos es buscar un índice específico.
El nuevo fragmento de código es el siguiente:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
DECLARE l_json_text VARCHAR2(32767); l_count PLS_INTEGER; l_members WWV_FLOW_T_VARCHAR2; l_paths APEX_T_VARCHAR2; l_exists BOOLEAN; BEGIN l_json_text := '{ "department": { "department_number": 10, "department_name": "ACCOUNTING", "employees": [ { "employee_number": 7782, "employee_name": "CLARK" }, { "employee_number": 7839, "employee_name": "KING" }, { "employee_number": 7934, "employee_name": "MILLER" } ] }, "metadata": { "published_date": "10-JUN-2024", "publisher": "expertos-oracle-apex.com" } }'; apex_json.parse(l_json_text); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Departamento (Búsqueda Ruta Básica)'); DBMS_OUTPUT.put_line('Número de Departamento : ' || APEX_JSON.get_number(p_path => 'department.department_number')); DBMS_OUTPUT.put_line('Nombre de Departamento : ' || APEX_JSON.get_varchar2(p_path => 'department.department_name')); DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Información de Empleados (Ciclo por el arreglo)'); l_count := APEX_JSON.get_count(p_path => 'department.employees'); DBMS_OUTPUT.put_line('Número de Empleados : ' || l_count); FOR i IN 1 .. l_count LOOP DBMS_OUTPUT.put_line('Indice de empleado : ' || i); DBMS_OUTPUT.put_line('Número de empleado : ' || APEX_JSON.get_number(p_path => 'department.employees[%d].employee_number', p0 => i)); DBMS_OUTPUT.put_line('Nombre de empleado : ' || APEX_JSON.get_varchar2(p_path => 'department.employees[%d].employee_name', p0 => i)); END LOOP; DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Verificar elementos (miembros) dentro de ruta'); l_members := APEX_JSON.get_members(p_path=>'department'); DBMS_OUTPUT.put_line('Número de Miembros : ' || l_members.COUNT); FOR i IN 1 .. l_members.COUNT LOOP DBMS_OUTPUT.put_line('Indice de Miembro : ' || i); DBMS_OUTPUT.put_line('Nombre de Miembro : ' || l_members(i)); END LOOP; DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Buscar por elementos coincidentes en un arreglo'); l_paths := APEX_JSON.find_paths_like( p_return_path => 'department.employees[%]', p_subpath => '.employee_name', p_value => 'MILLER' ); DBMS_OUTPUT.put_line('Rutas coincidentes : ' || l_paths.COUNT); FOR i IN 1 .. l_paths.COUNT loop DBMS_OUTPUT.put_line('Número de Empleado : ' || APEX_JSON.get_number(p_path => l_paths(i)||'.employee_number')); DBMS_OUTPUT.put_line('Nombre de Empleado : ' || APEX_JSON.get_varchar2(p_path => l_paths(i)||'.employee_name')); END LOOP; DBMS_OUTPUT.put_line('--------------------------------------------------'); DBMS_OUTPUT.put_line('Verificar si ruta existe'); l_exists := APEX_JSON.does_exist ( p_path => 'department.employees[%d].employee_name', p0 => 4); DBMS_OUTPUT.put('Empleado 4 Existe? : '); IF l_exists THEN DBMS_OUTPUT.put_line('Verdadero'); ELSE DBMS_OUTPUT.put_line('Falso'); END IF; END; |
Y el resultado se muestra a continuación.
Para conocer má ssobre la API APEX_JSON en APEX 26.1 y JSON, puede visitar los enlaces.












