I need to do this with Oracle 19c.
I have arbitrary json data in a column of a table.
I mean, I dont know the names of attributes, not even the deep the data is…
Suppose that I can identify the json objects in witch I am interested because all of them have an "id", "type", "text", and "call" attributes, but I dont know if is the root object or is under any other object, even in any array of any other object.
I want to do a query that finds in a json field, if it contains the object with type=t, text=x, and call=c.
It seems easy to find if there is an objects that have any of them have type=t, others have text=x, and others have call=c (but not consistently of the same object)
WITH Data AS (
SELECT '1' AS id, '{id:"1",type:"menu",text:"option1",call:"option1()"}' AS json FROM DUAL UNION ALL
-- next Select 2a- .. from dual, all in one line, formated only for better view:
SELECT '2-onlySubElements' AS id,
'{id:"2a",type:"menu",text:"option2",call:"option2()",
subElements:[
{id:"2.1",type:"menu",text:"option2.1",call:"option21()",
subElements:[{id:"2.1.1",type:"menu",text:"option2.1.1",call:"option211()"} ]},
{id:"2.2",type:"menu",text:"option2.2",call:"option22()"}
]
}' AS json FROM DUAL UNION ALL
-- next Select 2b-mix .. from dual, all in one line, formated only for better view:
SELECT '2b-mixOfInnerElements' AS id,
'{id:"2b",type:"menu",text:"option3",call:"option3()",
subElements:[
{id:"2.1",type:"menu",text:"option2.1",call:"option21()",
innerElements:[{id:"2.1.1",type:"menu",text:"option2.1.1",call:"option211()"} ]},
{id:"2.2",type:"menu",text:"option2.2",call:"option22()"}
]
}' AS json FROM DUAL UNION ALL
SELECT '0' AS id, '{id:"0",type:"label",text:"label0"}' AS json FROM DUAL
)
SELECT rownum, JSON_VALUE(json,'$.type'), a.*
FROM Data a
WHERE
-- uncomment one of the following options:
-- json_exists(json, '$?(@..type=="menu" && @..text=="option2" && @..call=="option21()" )'); -- BAD. it says exists, but attributes are not of the same element.
-- json_exists(json, '$.subElements[*]?(@.type=="menu" && @.text=="option2.1" && @.call=="option21()" )'); --NOT GOOD: This finds consistent elements in '2a-onlySubElements' and '2b-mixOfInnerElements'. But it needs the path.
-- json_exists(json, '$.subElements[*].subElements[*]?(@.type=="menu" && @.text=="option2.1.1" && @.call=="option211()" )'); --NOT GOOD: This finds one consistent element in '2a-onlySubElements'. But it needs the path
-- json_exists(json, '$.subElements[*]?(@.type=="menu" && @.text=="option2.1.1" && @.call=="option211()" )'); --BAD: This finds nothing (the path is not good)
How can I find a consistent object independently of where the object is inside of the root, or even if the object is the root?
Thank you.
(if this is not possible with oracle19, it could be possible with oracle21? Only to know, but I cant change oracle version)
2
Answers
Using json_dataguide you get the list of paths per id containing objects having the 4 fields (id, type, text, call):
To go further you will need to write PL/SQL code because you can’t use expressions in JSON functions like json_table for PATH accepting only constants, so you will need to use EXECUTE IMMEDIATE.
Example of whole solution: