Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4603903

Browse files
committed
Allow json{b}_strip_nulls to remove null array elements
An additional paramater ("strip_in_arrays") is added to these functions. It defaults to false. If true, then null array elements are removed as well as null valued object fields. JSON that just consists of a single null is not affected. Author: Florents Tselai <florents.tselai@gmail.com> Discussion: https://postgr.es/m/4BCECCD5-4F40-4313-9E98-9E16BEB0B01D@gmail.com
1 parent 5ead85f commit 4603903

File tree

8 files changed

+190
-9
lines changed

8 files changed

+190
-9
lines changed

doc/src/sgml/func.sgml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17345,25 +17345,32 @@ ERROR: value too long for type character(2)
1734517345
<indexterm>
1734617346
<primary>json_strip_nulls</primary>
1734717347
</indexterm>
17348-
<function>json_strip_nulls</function> ( <type>json</type> )
17348+
<function>json_strip_nulls</function> ( <parameter>target</parameter> <type>jsonb</type>, <optional>,<parameter>strip_in_arrays</parameter> <type>boolean</type> </optional> )
1734917349
<returnvalue>json</returnvalue>
1735017350
</para>
1735117351
<para role="func_signature">
1735217352
<indexterm>
1735317353
<primary>jsonb_strip_nulls</primary>
1735417354
</indexterm>
17355-
<function>jsonb_strip_nulls</function> ( <type>jsonb</type> )
17355+
<function>jsonb_strip_nulls</function> ( <parameter>target</parameter> <type>jsonb</type>, <optional>,<parameter>strip_in_arrays</parameter> <type>boolean</type> </optional> )
1735617356
<returnvalue>jsonb</returnvalue>
1735717357
</para>
1735817358
<para>
1735917359
Deletes all object fields that have null values from the given JSON
17360-
value, recursively. Null values that are not object fields are
17361-
untouched.
17360+
value, recursively.
17361+
If <parameter>strip_in_arrays</parameter> is true (the default is false),
17362+
null array elements are also stripped.
17363+
Otherwise they are not stripped. Bare null values are never stripped.
1736217364
</para>
1736317365
<para>
1736417366
<literal>json_strip_nulls('[{"f1":1, "f2":null}, 2, null, 3]')</literal>
1736517367
<returnvalue>[{"f1":1},2,null,3]</returnvalue>
17366-
</para></entry>
17368+
</para>
17369+
<para>
17370+
<literal>jsonb_strip_nulls('[1,2,null,3,4]', true);</literal>
17371+
<returnvalue>[1,2,3,4]</returnvalue>
17372+
</para>
17373+
</entry>
1736717374
</row>
1736817375

1736917376
<row>

src/backend/catalog/system_functions.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,20 @@ LANGUAGE INTERNAL
607607
STRICT STABLE PARALLEL SAFE
608608
AS 'jsonb_path_query_first_tz';
609609

610+
CREATE OR REPLACE FUNCTION
611+
jsonb_strip_nulls(target jsonb, strip_in_arrays boolean DEFAULT false)
612+
RETURNS jsonb
613+
LANGUAGE INTERNAL
614+
STRICT STABLE PARALLEL SAFE
615+
AS 'jsonb_strip_nulls';
616+
617+
CREATE OR REPLACE FUNCTION
618+
json_strip_nulls(target json, strip_in_arrays boolean DEFAULT false)
619+
RETURNS json
620+
LANGUAGE INTERNAL
621+
STRICT STABLE PARALLEL SAFE
622+
AS 'json_strip_nulls';
623+
610624
-- default normalization form is NFC, per SQL standard
611625
CREATE OR REPLACE FUNCTION
612626
"normalize"(text, text DEFAULT 'NFC')

src/backend/utils/adt/jsonfuncs.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ typedef struct StripnullState
286286
JsonLexContext *lex;
287287
StringInfo strval;
288288
bool skip_next_null;
289+
bool strip_in_arrays;
289290
} StripnullState;
290291

291292
/* structure for generalized json/jsonb value passing */
@@ -4460,8 +4461,19 @@ sn_array_element_start(void *state, bool isnull)
44604461
{
44614462
StripnullState *_state = (StripnullState *) state;
44624463

4463-
if (_state->strval->data[_state->strval->len - 1] != '[')
4464+
/* If strip_in_arrays is enabled and this is a null, mark it for skipping */
4465+
if (isnull && _state->strip_in_arrays)
4466+
{
4467+
_state->skip_next_null = true;
4468+
return JSON_SUCCESS;
4469+
}
4470+
4471+
/* Only add a comma if this is not the first valid element */
4472+
if (_state->strval->len > 0 &&
4473+
_state->strval->data[_state->strval->len - 1] != '[')
4474+
{
44644475
appendStringInfoCharMacro(_state->strval, ',');
4476+
}
44654477

44664478
return JSON_SUCCESS;
44674479
}
@@ -4493,6 +4505,7 @@ Datum
44934505
json_strip_nulls(PG_FUNCTION_ARGS)
44944506
{
44954507
text *json = PG_GETARG_TEXT_PP(0);
4508+
bool strip_in_arrays = PG_NARGS() == 2 ? PG_GETARG_BOOL(1) : false;
44964509
StripnullState *state;
44974510
JsonLexContext lex;
44984511
JsonSemAction *sem;
@@ -4503,6 +4516,7 @@ json_strip_nulls(PG_FUNCTION_ARGS)
45034516
state->lex = makeJsonLexContext(&lex, json, true);
45044517
state->strval = makeStringInfo();
45054518
state->skip_next_null = false;
4519+
state->strip_in_arrays = strip_in_arrays;
45064520

45074521
sem->semstate = state;
45084522
sem->object_start = sn_object_start;
@@ -4520,12 +4534,13 @@ json_strip_nulls(PG_FUNCTION_ARGS)
45204534
}
45214535

45224536
/*
4523-
* SQL function jsonb_strip_nulls(jsonb) -> jsonb
4537+
* SQL function jsonb_strip_nulls(jsonb, bool) -> jsonb
45244538
*/
45254539
Datum
45264540
jsonb_strip_nulls(PG_FUNCTION_ARGS)
45274541
{
45284542
Jsonb *jb = PG_GETARG_JSONB_P(0);
4543+
bool strip_in_arrays = false;
45294544
JsonbIterator *it;
45304545
JsonbParseState *parseState = NULL;
45314546
JsonbValue *res = NULL;
@@ -4534,6 +4549,9 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
45344549
JsonbIteratorToken type;
45354550
bool last_was_key = false;
45364551

4552+
if (PG_NARGS() == 2)
4553+
strip_in_arrays = PG_GETARG_BOOL(1);
4554+
45374555
if (JB_ROOT_IS_SCALAR(jb))
45384556
PG_RETURN_POINTER(jb);
45394557

@@ -4564,6 +4582,11 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
45644582
(void) pushJsonbValue(&parseState, WJB_KEY, &k);
45654583
}
45664584

4585+
/* if strip_in_arrays is set, also skip null array elements */
4586+
if (strip_in_arrays)
4587+
if (type == WJB_ELEM && v.type == jbvNull)
4588+
continue;
4589+
45674590
if (type == WJB_VALUE || type == WJB_ELEM)
45684591
res = pushJsonbValue(&parseState, type, &v);
45694592
else

src/include/catalog/pg_proc.dat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9269,7 +9269,7 @@
92699269
proname => 'to_json', provolatile => 's', prorettype => 'json',
92709270
proargtypes => 'anyelement', prosrc => 'to_json' },
92719271
{ oid => '3261', descr => 'remove object fields with null values from json',
9272-
proname => 'json_strip_nulls', prorettype => 'json', proargtypes => 'json',
9272+
proname => 'json_strip_nulls', prorettype => 'json', proargtypes => 'json bool',
92739273
prosrc => 'json_strip_nulls' },
92749274

92759275
{ oid => '3947',
@@ -10205,7 +10205,7 @@
1020510205
prorettype => 'jsonb', proargtypes => '',
1020610206
prosrc => 'jsonb_build_object_noargs' },
1020710207
{ oid => '3262', descr => 'remove object fields with null values from jsonb',
10208-
proname => 'jsonb_strip_nulls', prorettype => 'jsonb', proargtypes => 'jsonb',
10208+
proname => 'jsonb_strip_nulls', prorettype => 'jsonb', proargtypes => 'jsonb bool',
1020910209
prosrc => 'jsonb_strip_nulls' },
1021010210

1021110211
{ oid => '3478',

src/test/regress/expected/json.out

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,6 +2504,56 @@ select json_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }');
25042504
{"a":{},"d":{}}
25052505
(1 row)
25062506

2507+
-- json_strip_nulls (strip_in_arrays=true)
2508+
select json_strip_nulls(null, true);
2509+
json_strip_nulls
2510+
------------------
2511+
2512+
(1 row)
2513+
2514+
select json_strip_nulls('1', true);
2515+
json_strip_nulls
2516+
------------------
2517+
1
2518+
(1 row)
2519+
2520+
select json_strip_nulls('"a string"', true);
2521+
json_strip_nulls
2522+
------------------
2523+
"a string"
2524+
(1 row)
2525+
2526+
select json_strip_nulls('null', true);
2527+
json_strip_nulls
2528+
------------------
2529+
null
2530+
(1 row)
2531+
2532+
select json_strip_nulls('[1,2,null,3,4]', true);
2533+
json_strip_nulls
2534+
------------------
2535+
[1,2,3,4]
2536+
(1 row)
2537+
2538+
select json_strip_nulls('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}', true);
2539+
json_strip_nulls
2540+
-------------------------------
2541+
{"a":1,"c":[2,3],"d":{"e":4}}
2542+
(1 row)
2543+
2544+
select json_strip_nulls('[1,{"a":1,"b":null,"c":2},3]', true);
2545+
json_strip_nulls
2546+
---------------------
2547+
[1,{"a":1,"c":2},3]
2548+
(1 row)
2549+
2550+
-- an empty object is not null and should not be stripped
2551+
select json_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }', true);
2552+
json_strip_nulls
2553+
------------------
2554+
{"a":{},"d":{}}
2555+
(1 row)
2556+
25072557
-- json to tsvector
25082558
select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::json);
25092559
to_tsvector

src/test/regress/expected/jsonb.out

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4153,6 +4153,56 @@ select jsonb_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }');
41534153
{"a": {}, "d": {}}
41544154
(1 row)
41554155

4156+
-- jsonb_strip_nulls (strip_in_arrays=true)
4157+
select jsonb_strip_nulls(null, true);
4158+
jsonb_strip_nulls
4159+
-------------------
4160+
4161+
(1 row)
4162+
4163+
select jsonb_strip_nulls('1', true);
4164+
jsonb_strip_nulls
4165+
-------------------
4166+
1
4167+
(1 row)
4168+
4169+
select jsonb_strip_nulls('"a string"', true);
4170+
jsonb_strip_nulls
4171+
-------------------
4172+
"a string"
4173+
(1 row)
4174+
4175+
select jsonb_strip_nulls('null', true);
4176+
jsonb_strip_nulls
4177+
-------------------
4178+
null
4179+
(1 row)
4180+
4181+
select jsonb_strip_nulls('[1,2,null,3,4]', true);
4182+
jsonb_strip_nulls
4183+
-------------------
4184+
[1, 2, 3, 4]
4185+
(1 row)
4186+
4187+
select jsonb_strip_nulls('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}', true);
4188+
jsonb_strip_nulls
4189+
--------------------------------------
4190+
{"a": 1, "c": [2, 3], "d": {"e": 4}}
4191+
(1 row)
4192+
4193+
select jsonb_strip_nulls('[1,{"a":1,"b":null,"c":2},3]', true);
4194+
jsonb_strip_nulls
4195+
--------------------------
4196+
[1, {"a": 1, "c": 2}, 3]
4197+
(1 row)
4198+
4199+
-- an empty object is not null and should not be stripped
4200+
select jsonb_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }', true);
4201+
jsonb_strip_nulls
4202+
--------------------
4203+
{"a": {}, "d": {}}
4204+
(1 row)
4205+
41564206
select jsonb_pretty('{"a": "test", "b": [1, 2, 3], "c": "test3", "d":{"dd": "test4", "dd2":{"ddd": "test5"}}}');
41574207
jsonb_pretty
41584208
----------------------------

src/test/regress/sql/json.sql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,25 @@ select json_strip_nulls('[1,{"a":1,"b":null,"c":2},3]');
814814
-- an empty object is not null and should not be stripped
815815
select json_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }');
816816

817+
-- json_strip_nulls (strip_in_arrays=true)
818+
819+
select json_strip_nulls(null, true);
820+
821+
select json_strip_nulls('1', true);
822+
823+
select json_strip_nulls('"a string"', true);
824+
825+
select json_strip_nulls('null', true);
826+
827+
select json_strip_nulls('[1,2,null,3,4]', true);
828+
829+
select json_strip_nulls('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}', true);
830+
831+
select json_strip_nulls('[1,{"a":1,"b":null,"c":2},3]', true);
832+
833+
-- an empty object is not null and should not be stripped
834+
select json_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }', true);
835+
817836
-- json to tsvector
818837
select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::json);
819838

src/test/regress/sql/jsonb.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,24 @@ select jsonb_strip_nulls('[1,{"a":1,"b":null,"c":2},3]');
11021102
-- an empty object is not null and should not be stripped
11031103
select jsonb_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }');
11041104

1105+
-- jsonb_strip_nulls (strip_in_arrays=true)
1106+
1107+
select jsonb_strip_nulls(null, true);
1108+
1109+
select jsonb_strip_nulls('1', true);
1110+
1111+
select jsonb_strip_nulls('"a string"', true);
1112+
1113+
select jsonb_strip_nulls('null', true);
1114+
1115+
select jsonb_strip_nulls('[1,2,null,3,4]', true);
1116+
1117+
select jsonb_strip_nulls('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}', true);
1118+
1119+
select jsonb_strip_nulls('[1,{"a":1,"b":null,"c":2},3]', true);
1120+
1121+
-- an empty object is not null and should not be stripped
1122+
select jsonb_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }', true);
11051123

11061124
select jsonb_pretty('{"a": "test", "b": [1, 2, 3], "c": "test3", "d":{"dd": "test4", "dd2":{"ddd": "test5"}}}');
11071125
select jsonb_pretty('[{"f1":1,"f2":null},2,null,[[{"x":true},6,7],8],3]');

0 commit comments

Comments
 (0)