Easier, Better, Faster JSON: 12 Things Developers Will Love About Oracle Database 12c Release 2 Part 1
By: Chris Saxon
Tags: Json, Oracle Database, Sql, Programming

12.1.0.2 brought JSON support to Oracle Database. This helped you work with JSON documents stored in clobs or varchar2s.

These are fantastic. But storing raw JSON should be the exception, not the norm. Most of the time you should shred your JSON documents into relational tables.

This leaves you with a problem though. Getting the data back out in JSON format!

Trying to write your own JSON generator is hard. So in 12.2 we offer a whole host of options to help you get the job done.

JSON From SQL

12.2 provides four key functions to help you write SQL that returns data in JSON format:

JSON_object

JSON_objectagg

JSON_array

JSON_arrayagg

You use the JSON_object* functions to create series of key-value pair documents. i.e. the output has curly braces {}. The JSON_array* functions take a list of values and return it as an array i.e. in square brackets [].

For each row in the input, the non-agg versions of these functions output a row. The agg versions combine multiple rows into a single document or array.

OK. So how do these work?

Let’s look at an example.

Say you’re using the classic employees and departments tables. For each department you want a JSON document that contains:

The department name

An array of its employees

Each element of this array should be its own document, listing the employee’s name and their job title.

For example:

{

"department": "Accounting",

"employees": [ {

"name": "Shelley,Higgins",

"job": "Accounting Manager"

}, {

"name": "William,Gietz",

"job": "Public Accountant"

} ]

}

How do you create this using the new functions?

Let’s work from the inside out:

First you need a document for each employee. This has two attributes, name and job. Pass these into a JSON_object call.

Then you need to turn these into an array. So wrap the JSON_object in a JSON_arrayagg. Group by department to split out the employees for each one into a separate array.

Finally you have a document per department. So you need another JSON_object with department and employees attributes. The values for these are the department name and the results of the JSON_arrayagg call in the previous step.

Put it all together and you get:

select json_object (

'department' value d.department_name,

'employees' value json_arrayagg (

json_object (

'name' value first_name || ',' || last_name,

'job' value job_title

)

)

)

from hr.departments d, hr.employees e, hr.jobs j

where d.department_id = e.department_id

and e.job_id = j.job_id

group by d.department_name;

And voila! You have your JSON!

JSON in PL/SQL

So now you have your JSON document. But what if you want to edit it?

Say you want to change the names to uppercase. And add a title element. So the previous document becomes:

{

"department": "Accounting",

"employees": [ {

"name": "SHELLEY,HIGGINS",

"job": "Accounting Manager", "title": ""

}, {

"name": "WILLIAM,GIETZ",

"job": "Public Accountant",

"title": ""

} ]

}

If you’re generating the document it’s easiest to add these in the SQL! So this assumes you want to change a JSON document from an external source.

To help with this, there are new PL/SQL objects. These enable you to access, modify and add elements to a JSON document with get/put calls.

The key object types are:

json_element_t — a supertype for documents and arrays

json_object_t — for working with JSON documents

json_array_t — for working with JSON arrays

The first thing you need to do is create the JSON object. Do this by parsing the document:

doc := json_object_t.parse(' {

"department": "Accounting",

"employees": [ {

"name": "Shelley,Higgins",

"job": "Accounting Manager"

}, {

"name": "William,Gietz",

"job": "Public Accountant" } ]

} '

);

You can then access the employees array using get:

emps := treat(doc.get('employees') as json_array_t) ;

The treat function casts the element to the appropriate type (JSON_array_t here).

Once you have the array, you can loop through the employees. Put adds a new key if it’s not present. Otherwise it overwrites the existing value.

for i in 0 .. emps.get_size - 1 loop

emp := treat(emps.get(i) as json_object_t);

emp.put('title', '');

emp.put('name', upper(emp.get_String('name')));

end loop;

The get functions return a reference to the original object. So if you get some JSON and modify it, the original document also changes!

If you don’t want this, clone the element when you get it. For example:

emps := treat(doc.get('employees') as json_array_t).clone

So the complete PL/SQL block to transform the JSON is:

declare

doc json_object_t;

emps json_array_t;

emp json_object_t;

begin

doc := json_object_t.parse('{

"department": "Accounting",

"employees": [ {

"name": "Shelley,Higgins",

"job": "Accounting Manager"

}, {

"name": "William,Gietz",

"job": "Public Accountant"

} ]

}'

); emps := treat(doc.get('employees') as json_array_t) ; for i in 0 .. emps.get_size - 1 loop

emp := treat(emps.get(i) as json_object_t);

emp.put('title', '');

emp.put('name', upper(emp.get_String('name')));

end loop; dbms_output.put_line(doc.to_String);

end;

/

{

"department": "Accounting",

"employees": [ {

"name": "SHELLEY,HIGGINS",

"job": "Accounting Manager",

"title": ""

}, {

"name": "WILLIAM,GIETZ",

"job": "Public Accountant",

"title": ""

} ]

}

Now you can generate JSON from SQL and change it in PL/SQL you have powerful options to work with it.

And there’s a raft of other improvements to JSON functionality in 12.2. Other enhancements include:

JSON_exists function

Support for In-Memory, Partitioning and Materialized Views

Search indexes

GeoJSON

JSON Data Guide

If you’re desperate to work with JSON, I recommend checking these out.