'Generate JSON object from a dynamic set of input functions
I am trying to write a function (f_all) that basically just calls other "partial" functions (e.g. f_1
,f_2
,f_3
) and merges the results of them into one final JSON object. Function f_all
will take a string array (e.g '1,2,3') and for every number in the array, it will add corresponding key in the resulting JSON , e.g. "part1" and value, which will be a result of the "partial" function.
So for example, for f_all('1,2,3') it should return JSON:
{"part1": {<result of f_1>},
"part2": {<result of f_2>},
"part3": {<result of f_3>}
}
I managed to achieve something similar using a FOR
loop to append the result of each "partial" function to resulting array with array_append()
. However, I need the result to be a simple JSON/JSONB object, not an array of objects.
This works and returns an array of json objects:
CREATE OR REPLACE FUNCTION f_all(ingeom geometry, parts text)
RETURNS jsonb []
LANGUAGE plpgsql
AS $function$
DECLARE
arr TEXT ARRAY;
i text;
json_partx jsonb;
resultjson_part jsonb;
resultjson jsonb;
resultarray json [];
BEGIN
arr = string_to_array(parts, ',');
FOREACH i in ARRAY arr
LOOP
EXECUTE 'SELECT f_' || i || '($1)' into json_partx using ingeom;
resultjson_part = json_build_object('part' || i, json_partx);
resultjson = array_append(resultjson, resultjson_part);
END LOOP;
RETURN (resultjson);
END;
$function$;
In a similar manner, I want, in each loop iteration, to append key and value pairs into JSON object and return the resulting object.
Solution 1:[1]
You write:
Function f_all will take a string array (e.g
'1,2,3'
)
But that's not an array. It's a list of values passed as plain text. Pass an actual array instead. Better yet, use a VARIADIC
function so you can pass an array or a list of values.
Your function is open to SQL injection, which is a big no-go.
Also worth mentioning that the data type geometry
used here requires the additional module PostGis installed.
Assuming all your functions f_1()
... f_N()
return a scalar result (not a set!), consider this radically simpler, faster and safer function:
CREATE OR REPLACE FUNCTION f_all(_ingeom geometry, VARIADIC _parts int[], OUT _result jsonb)
LANGUAGE plpgsql AS
$func$
BEGIN
IF _parts <> '{}' THEN -- rule out NULL and empty array as input
EXECUTE 'SELECT to_jsonb(t.*) FROM (SELECT f_' || array_to_string(_parts, '($1), f_')
|| '($1)) AS t(part'
|| array_to_string(_parts, ', part') || ')'
INTO _result
USING _ingeom;
ELSE
RAISE EXCEPTION 'Parameter "_parts" must not be empty or NULL!';
END IF;
END
$func$;
Returns a JSON object (as jsonb
) as requested.
Call:
SELECT f_all('foo', 1, 2, 3);
Or:
SELECT f_all('foo', VARIADIC '{1,2,3}');
The function takes integer
input, which rules out SQL injection a priori.
Using a subselect to conveniently build the JSON object. Alternatively, you could use jsonb_object()
or jsonb_build_object()
. See:
Using a VARIADIC
function for convenience. See:
- Pass multiple values in single parameter
- Return rows matching elements of input array in plpgsql function
Using an OUT
parameter for convenience. See:
- Returning from a function with OUT parameter
- Can I make a plpgsql function return an integer without using a variable?
To address your original (now void) question: use the concatenation operator ||
to "append" JSON objects:
SELECT jsonb '{"foo":1}' || jsonb '{"bar":"baz"}';
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 |