Home > Software design >  Retrieving JSON Array data from Oracle CLOB column
Retrieving JSON Array data from Oracle CLOB column

Time:09-20

I am having an issue getting data from a JSON array attribute inside some JSON data which is stored in a CLOB column. I am working in an Oracle 19 database (some of the queries were modified to obfuscate the system I am working on).

The raw JSON (before loading into CLOB) looks like this:

[
    {
        "displayName":  "McGuff, Bubba",
        "ismemberof":  [
                           "cn=Active User,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api",
                           "cn=Base Environmental Coordinators,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api"
                       ],
        "employeeType":  "Worker"
    },
    {
        "displayName":  "Grumpy, Maurice",
        "ismemberof":  "cn=Active User,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api",
        "employeeType":  "Manager"
    },
    {
        "displayName":  "Syrimon, Quentin",
        "ismemberof":  null,
        "employeeType":  "HR"
    }
]

I first tried this query to retrieve the data:

SELECT j.process_id, 
       jt.display_Name, 
       jt.is_member_of,
       jt.employee_Type 
FROM   My_Process j
       cross apply JSON_TABLE(j.My_DATA, '$[*]'
         COLUMNS (display_Name                  VARCHAR2(200 CHAR)  PATH '$.displayName',
                  is_Member_Of                  VARCHAR2(4000 CHAR) PATH '$.ismemberof[*]',
                  employee_Type                 VARCHAR2(200 CHAR)  PATH '$.employeeType')) jt
where process_id =85
order by j.process_id;

This is what is returned:

PROCESS_ID   DISPLAY_NAME     IS_MEMBER_OF   EMPLOYEE_TYPE
85           McGuff, Bubba                   Worker
85           Grumpy, Maurice  cn=Active User,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api 
  Manager
85           Syrimon, Quentin                HR

So the only person that is showing an IS_MEMBER_OF attribute is Maurice. There should be a value for Bubba also.

I looked around and found this function that I thought would help but it didn't

create or replace FUNCTION json_array_to_string_tbl ( 
    p_json_array IN VARCHAR2 
    ) RETURN string_tbl_t 
    is 
      l_string_tbl string_tbl_t:= string_tbl_t(); 
    begin 
       if p_json_array is not null and length(p_json_array)>0 
       then 
           SELECT value 
           bulk collect into l_string_tbl 
           FROM json_table( p_json_array, '$[*]' 
                            COLUMNS (value PATH '$' 
                           ) 
          );  
       end if; 
       return l_string_tbl; 
    end json_array_to_string_tbl;

I then used it in this query:

SELECT j.process_id, 
       jt.display_Name, 
       json_array_to_string_tbl(jt.is_member_of) as isMemberOf,
       jt.employee_Type 
FROM   My_Process j
       cross apply JSON_TABLE(j.My_DATA, '$[*]'
         COLUMNS (display_Name                  VARCHAR2(200 CHAR)  PATH '$.displayName',
                  is_Member_Of                  VARCHAR2(4000 CHAR) PATH '$.ismemberof[*]',
                  employee_Type                 VARCHAR2(200 CHAR)  PATH '$.employeeType')) jt
where process_id =85
order by j.process_id;

The result for the Process_ID, Display_Name, and Employee_Type were the same. The IsMemberOf showed (DATASET) as the value. When I double-clicked on (DATASET), it showed an empty grid. So no values were returned.

What is the best way to retrieve the values from the IsMemberOf attribute when it is both a JSON Array and when it is just a single string.

Thanks

CodePudding user response:

The best solution is to fix your JSON so that ismemberof always contains an array.


A secondary solution is to extract both the string and array values of ismemberof and then use COALESCE to combine them into a value that is always a JSON array:

SELECT j.process_id, 
       jt.display_name,
       jt.employee_type,
       m.is_member_of
FROM   My_Process j
       CROSS APPLY JSON_TABLE(
         j.My_DATA,
         '$[*]'
         COLUMNS (
           display_Name     VARCHAR2(200 CHAR) PATH '$.displayName',
           is_Member_Of_arr CLOB FORMAT JSON   PATH '$.ismemberof',
           is_Member_Of_str CLOB               PATH '$.ismemberof',
           employee_Type    VARCHAR2(200 CHAR) PATH '$.employeeType'
         )
       ) jt
       OUTER APPLY JSON_TABLE(
         COALESCE(jt.is_member_of_arr, '["' || jt.is_member_of_str || '"]'),
         '$[*]'
         COLUMNS(
           is_member_of CLOB PATH '$'
         )
       ) m
where process_id =85
order by j.process_id;

Which, for your sample data:

CREATE TABLE My_Process (
  process_id NUMBER,
  my_data CLOB CHECK (my_data IS JSON)
);

INSERT INTO My_Process (process_id, my_data) VALUES (85, '[
    {
        "displayName":  "McGuff, Bubba",
        "ismemberof":  [
                           "cn=Active User,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api",
                           "cn=Base Environmental Coordinators,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api"
                       ],
        "employeeType":  "Worker"
    },
    {
        "displayName":  "Grumpy, Maurice",
        "ismemberof":  "cn=Active User,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api",
        "employeeType":  "Manager"
    },
    {
        "displayName":  "Syrimon, Quentin",
        "ismemberof":  null,
        "employeeType":  "HR"
    }
]');

Outputs:

PROCESS_ID DISPLAY_NAME EMPLOYEE_TYPE IS_MEMBER_OF
85 McGuff, Bubba Worker cn=Active User,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api
85 McGuff, Bubba Worker cn=Base Environmental Coordinators,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api
85 Grumpy, Maurice Manager cn=Active User,ou=roles,ou=currentstate,ou=MyApp,ou=IBM,o=api
85 Syrimon, Quentin HR null

fiddle

  • Related