If there is no way of knowing how deep the recursion will be, then XML would be easier because Oracle supports the XQUERY language. For JSON, I tried using recursive subquery factoring:
with q1(dad, son, dad_cond, CONDITION, VALID, RULES, ID, FIELD, TYPE, INPUT, OPERATOR, VALUE) as (
select null, lvl, null, CONDITION, VALID, RULES, ID, FIELD, TYPE, INPUT, OPERATOR, VALUE
from t,
json_table(jdoc, '$'
columns (
condition varchar2(10) path '$.condition',
valid varchar2(10) path '$.valid',
nested path '$.rules[*]'
columns (
lvl for ordinality,
rules varchar2(4000) format json without wrapper path '$',
id varchar2(10) path '$.id',
field varchar2(10) path '$.field',
type varchar2(10) path '$.type',
input varchar2(10) path '$.input',
operator varchar2(10) path '$.operator',
value varchar2(10) path '$.value'
)
)
) n
union all
select o.son, n.lvl, o.CONDITION, n.CONDITION, o.VALID, n.RULES, n.ID, n.FIELD, n.TYPE, n.INPUT, n.OPERATOR, n.VALUE
from q1 o,
json_table(rules, '$'
columns (
condition varchar2(10) path '$.condition',
nested path '$.rules[*]'
columns (
lvl for ordinality,
rules varchar2(4000) format json without wrapper path '$',
id varchar2(10) path '$.id',
field varchar2(10) path '$.field',
type varchar2(10) path '$.type',
input varchar2(10) path '$.input',
operator varchar2(10) path '$.operator',
value varchar2(10) path '$.value'
)
)
) n
where o.rules is not null and o.id is null
) search depth first by valid set order1
select
start_paren || field ||' '|| operator ||' '|| value ||' '|| end_paren
as txt
from (
select order1, dad, son, CONDITION, VALID, ID, FIELD, TYPE, INPUT, OPERATOR, VALUE,
case when son = 1 and dad is not null then '( ' end start_paren,
case when order1 = max(order1) over() and dad is null then null
when order1 = max(order1) over() then ')'
when son = max(son) over(partition by dad) then ') ' || dad_cond
else condition
end end_paren
from q1
)
where id is not null
order by order1;
TXT
-----------------------
price less 10.25 AND
( category equal 2 OR
category equal 1 ) AND
( name contains test OR
in_stock equal 0 )
Do a LISTAGG to get everything on the same line.