'How to extract data from json object and json array in same POSTGRESQL query

I have a column(name: additional_information) in the database having mode of payments recorded in Json Format like this:

First Scenario:

{
   "cash":{
      "use":1,
      "amount":"285"
   },
   "card_payment":[
      {
         "method":"Debit Card",
         "use":1,
         "amount":"5900",
         "details":"081958"
      },
      {
         "method":"Debit Card",
         "use":1,
         "amount":"3245",
         "details":"379613"
      }
   ],
   "method_title":"POS Payment"
}

Second Scenario:

{
   "method_title":"Cash On Delivery",
   "instructions":""
}

Third Scenaario:

{
   "others":[
      {
         "use":1,
         "amount":"649",
         "method":"Phonepe",
         "details":"2022040089 "
      },
      {
         "use":1,
         "amount":"59",
         "method":"Phonepe",
         "details":"T22040584749376 "
      }
   ],
   "method_title":"POS Payment"
}

I want to extract all the necessary info out of this For example

cash
card
others others_type

I have tried this:

 select replace(json_extract_path(sop.additional_information::json,'cash','amount')::text,'"','') AS cash,
              json_array_elements_text(json_extract_path(sop.additional_information::json,'card_payment'))::json->>'amount' AS card,
              json_array_elements_text(json_extract_path(sop.additional_information::json,'others'))::json->>'amount' AS custom,
              json_array_elements_text(json_extract_path(sop.additional_information::json,'others'))::json->>'method' AS custom_type,
              case when sop.method = 'cashondelivery' then so.grand_total::text end as cash2,
              case when sop.method = 'paytm' then so.grand_total::text end as custom2 from table sop

I am finding difficulty in adding all the cards value if there are more than 1 card used. I hope I explained my questions clearly. Thanks in advance.



Solution 1:[1]

For extracting elements (keys and values) you can use jsonb_each function. For extracting array elements you can use jsonb_array_elements function. And also you can use jsonb_typeof for gets type of key values. For example:

select 
    t2.pkey,
    t2.pvalue,   
    jsonb_typeof(t2.pvalue) as valuetype 
from sample_table t1 
cross join jsonb_each(t1.json_text) t2(pkey, pvalue)

Return:

pkey pvalue valuetype
cash {"use": 1, "amount": "285"} object
card_payment [{"use": 1, "amount": "5900", "method": "Debit Card", "details": "081958"}, {"use": 1, "amount": "3245", "method": "Debit Card", "details": "379613"}] array
method_title "POS Payment" string
others [{"use": 1, "amount": "649", "method": "Phonepe", "details": "2022040089 "}, {"use": 1, "amount": "59", "method": "Phonepe", "details": "T22040584749376 "}] array
method_title "POS Payment" string
instructions "" string
method_title "Cash On Delivery" string

Which type of values is array or object, for these values you must use jsonb_each function again. But Recommended creating recursive function sub arrays and sub json's. I wrote an SQL query sample for extracting all keys and values, but without recursive.

with json_table as materialized (
    select 
        t2.pkey,
        t2.pvalue,   
        jsonb_typeof(t2.pvalue) as valuetype 
    from test.users t1 
    cross join jsonb_each(t1.json_text) t2(pkey, pvalue)
    where t1.id in (1,3,123)
)
select pkey, pvalue from json_table where valuetype not in ('object', 'array')
union all 
select t2.pkey, t2.pvalue from json_table t1
cross join jsonb_each(t1.pvalue) t2(pkey, pvalue)
where t1.valuetype in ('object')
union all 
select m2.pkey, m2.pvalue 
from 
    (
        select t1.pkey, t2.pvalue from json_table t1
        cross join jsonb_array_elements(t1.pvalue) t2(pvalue)
        where t1.valuetype in ('array')
    ) m1 
cross join jsonb_each(m1.pvalue) m2(pkey, pvalue);

And result of this query:

pkey pvalue
method_title "POS Payment"
method_title "POS Payment"
instructions ""
method_title "Cash On Delivery"
use 1
amount "285"
use 1
amount "5900"
method "Debit Card"
details "081958"
use 1
amount "3245"
method "Debit Card"
details "379613"
use 1
amount "649"
method "Phonepe"
details "2022040089 "
use 1
amount "59"
method "Phonepe"
details "T22040584749376 "

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ramin Faracov