Oracle REST Data Services provides a few features to make working with {JSON} as easy as possible. One of the fundamental ones is making the data from your POST requests readily accessible in your SQL and PL/SQL blocks.

But, as your incoming JSON gets more and more complicated, you might need to step in and write your own SQL to manipulate the document.

Here’s an example from our forums

The developer wants to take in this {JSON}, and shove it to a table:

{
  "device" : "37AB2C",
  "time" : "1552749097",
  "data" : "0d01da0c1600",
  "signal" : "Average",
  "location" : 
  {
   "lat":49.21706417063655,
   "lng":16.627916371512473,
   "radius":9228,
   "source":2,
   "status":1
  },
  "seqnumber": 1714
}

It’s quite easy to handle this up until we get to the nested document, e.g. ‘location.’

With ORDS, we can automatically reference the json doc values for device, time, data, signal, and seqnumber.

We could simply do an

INSERT INTO tableX VALUES (:device, :TIME, :DATA, :signal, :seqnumber);
commit;

But what about the location data?

We need to read in the POST BODY as a CLOB instead, and then use the database features for working with JSON to build out our INSERT.

Here’s our table:

  CREATE TABLE "FORUMS_NESTED_JSON_TABLE" 
   (	"DEVICE" VARCHAR2(10 BYTE), 
	"TIME" NUMBER(*,0), 
	"DATA" VARCHAR2(20 BYTE), 
	"SIGNAL" VARCHAR2(20 BYTE), 
	"SEQNUMBER" NUMBER(*,0), 
	"LAT" NUMBER(9,6), 
	"LNG" NUMBER(9,6), 
	"RADIUS" NUMBER(*,0), 
	"SOURCE" NUMBER(*,0), 
	"STATUS" NUMBER(*,0), 
	 CONSTRAINT "DEVICE_TIME_XPK" PRIMARY KEY ("DEVICE", "TIME")
  USING INDEX  ENABLE
   ) ;

Remember, starting with version 18.3, we can immediately access the POST body as a CLOB using the automatic bind variable, :body_text.

You can of course work with JSON in the database as a BLOB if you’d like (:body), or if you’re on an older copy of ORDS, just cast the BLOB as a CLOB in your SQL.

But anyway, using the JSON_TABLE function, we can access the data in the nested JSON document using the NESTED PATH syntax on ‘$.location[*].

Here’s my clumsy POST Handler code for doing the INSERT with ORDS:

BEGIN
INSERT INTO forums_nested_json_table
(device, TIME, data, signal, seqnumber, lat, lng, radius, source, status)
SELECT *
  FROM
    json_table(:body_text, '$'
        columns(
            "device" VARCHAR2(10)PATH '$.device',
            "time" NUMBER PATH '$.time',
            "data" VARCHAR2(15)PATH '$.data',
            "signal" VARCHAR2(10)PATH '$.signal',
            "seqnumber" NUMBER PATH '$.seqnumber',
            nested PATH '$.location[*]'
                columns(
                    LAT NUMBER PATH '$.lat',
                    LNG NUMBER PATH '$.lng',
                    RADIUS NUMBER PATH '$.radius',
                    SOURCE NUMBER PATH '$.source',
                    STATUS NUMBER PATH '$.status'
                )
        )
    );
COMMIT;
  :status := 201;
END;

I called my approach clumsy, because I could have also created a nested table or custom complex type to handle the LOCATION data as a single attribute/column of the table, but I went for the quick and dirty approach of one new column per location document attribute – lat, lng, radius, source, and status.

Let’s do the POST and see our new record.

It’s pretty boring code, but that’s the SIMPLICITY of SQL and ORDS working together to make things EASY and FAST.

Shout out to Beda – go follow and read his stuff.

Anytime I step into the world of JSON in the database, I ultimately find myself back on Beda’s blog. This one from 2015 gave me exactly what I was looking for.

Beda is our JSON in the Database expert and also frequently hosts Office Hours sessions on his favorite topic.

You can follow Beda on Twitter @bch_t

Author

I'm a Distinguished Product Manager at Oracle. My mission is to help you and your company be more efficient with our database tools.

Write A Comment