You can use json_to_recordset
function to convert JSON into rowset. Anyway, final rowset cannot have dynamic number of columns, i.e. whatever solution you choose, you will have to list them explicitly in some way.
For example in select clause when doing manual transposition of 1:1-converted JSON:
with t(d) as (values
('{"elements":[{"val":"value1", "column":"column1"}, {"val":"val2", "column":"column2"}]}'::json)
), matrix(val,col) as (
select x.val, x."column"
from t
inner join lateral json_to_recordset((t.d->>'elements')::json) as x(val text, "column" text) on true
)
select (select val from matrix where col = 'column1') as column1
, (select val from matrix where col = 'column2') as column2
Or in as x(column1 text, column2 text)
clause when using the crosstab
extension (see this question).
Or in somehow transformed or converted-to-xml JSON.