Skip to content

Commit 319a4f2

Browse files
committed
json: teach json builder "conditional" object fields
Quite often when we generate objects some fields should only be generated in some conditions. Let's add high-level support for that. Matching the existing JSON_BUILD_PAIR() this adds JSON_BUILD_PAIR_CONDITIONAL() which is very similar, but takes an additional parameter: a boolean condition. If "true" this acts like JSON_BUILD_PAIR(), but if false then the whole pair is suppressed. This sounds simply, but requires a tiny bit of complexity: when complex sub-variants are used in fields, then we also need to suppress them.
1 parent 2de6225 commit 319a4f2

File tree

3 files changed

+149
-34
lines changed

3 files changed

+149
-34
lines changed

src/shared/json.c

Lines changed: 131 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2249,6 +2249,7 @@ typedef struct JsonStack {
22492249
size_t n_elements, n_elements_allocated;
22502250
unsigned line_before;
22512251
unsigned column_before;
2252+
size_t n_suppress; /* When building: if > 0, suppress this many subsequent elements. If == (size_t) -1, suppress all subsequent elements */
22522253
} JsonStack;
22532254

22542255
static void json_stack_release(JsonStack *s) {
@@ -2656,6 +2657,8 @@ int json_buildv(JsonVariant **ret, va_list ap) {
26562657

26572658
for (;;) {
26582659
_cleanup_(json_variant_unrefp) JsonVariant *add = NULL;
2660+
size_t n_subtract = 0; /* how much to subtract from current->n_suppress, i.e. how many elements would
2661+
* have been added to the current variant */
26592662
JsonStack *current;
26602663
int command;
26612664

@@ -2679,9 +2682,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
26792682

26802683
p = va_arg(ap, const char *);
26812684

2682-
r = json_variant_new_string(&add, p);
2683-
if (r < 0)
2684-
goto finish;
2685+
if (current->n_suppress == 0) {
2686+
r = json_variant_new_string(&add, p);
2687+
if (r < 0)
2688+
goto finish;
2689+
}
2690+
2691+
n_subtract = 1;
26852692

26862693
if (current->expect == EXPECT_TOPLEVEL)
26872694
current->expect = EXPECT_END;
@@ -2703,9 +2710,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
27032710

27042711
j = va_arg(ap, intmax_t);
27052712

2706-
r = json_variant_new_integer(&add, j);
2707-
if (r < 0)
2708-
goto finish;
2713+
if (current->n_suppress == 0) {
2714+
r = json_variant_new_integer(&add, j);
2715+
if (r < 0)
2716+
goto finish;
2717+
}
2718+
2719+
n_subtract = 1;
27092720

27102721
if (current->expect == EXPECT_TOPLEVEL)
27112722
current->expect = EXPECT_END;
@@ -2727,9 +2738,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
27272738

27282739
j = va_arg(ap, uintmax_t);
27292740

2730-
r = json_variant_new_unsigned(&add, j);
2731-
if (r < 0)
2732-
goto finish;
2741+
if (current->n_suppress == 0) {
2742+
r = json_variant_new_unsigned(&add, j);
2743+
if (r < 0)
2744+
goto finish;
2745+
}
2746+
2747+
n_subtract = 1;
27332748

27342749
if (current->expect == EXPECT_TOPLEVEL)
27352750
current->expect = EXPECT_END;
@@ -2751,9 +2766,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
27512766

27522767
d = va_arg(ap, long double);
27532768

2754-
r = json_variant_new_real(&add, d);
2755-
if (r < 0)
2756-
goto finish;
2769+
if (current->n_suppress == 0) {
2770+
r = json_variant_new_real(&add, d);
2771+
if (r < 0)
2772+
goto finish;
2773+
}
2774+
2775+
n_subtract = 1;
27572776

27582777
if (current->expect == EXPECT_TOPLEVEL)
27592778
current->expect = EXPECT_END;
@@ -2775,9 +2794,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
27752794

27762795
b = va_arg(ap, int);
27772796

2778-
r = json_variant_new_boolean(&add, b);
2779-
if (r < 0)
2780-
goto finish;
2797+
if (current->n_suppress == 0) {
2798+
r = json_variant_new_boolean(&add, b);
2799+
if (r < 0)
2800+
goto finish;
2801+
}
2802+
2803+
n_subtract = 1;
27812804

27822805
if (current->expect == EXPECT_TOPLEVEL)
27832806
current->expect = EXPECT_END;
@@ -2796,9 +2819,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
27962819
goto finish;
27972820
}
27982821

2799-
r = json_variant_new_null(&add);
2800-
if (r < 0)
2801-
goto finish;
2822+
if (current->n_suppress == 0) {
2823+
r = json_variant_new_null(&add);
2824+
if (r < 0)
2825+
goto finish;
2826+
}
2827+
2828+
n_subtract = 1;
28022829

28032830
if (current->expect == EXPECT_TOPLEVEL)
28042831
current->expect = EXPECT_END;
@@ -2816,12 +2843,16 @@ int json_buildv(JsonVariant **ret, va_list ap) {
28162843
goto finish;
28172844
}
28182845

2846+
/* Note that we don't care for current->n_suppress here, after all the variant is already
2847+
* allocated anyway... */
28192848
add = va_arg(ap, JsonVariant*);
28202849
if (!add)
28212850
add = JSON_VARIANT_MAGIC_NULL;
28222851
else
28232852
json_variant_ref(add);
28242853

2854+
n_subtract = 1;
2855+
28252856
if (current->expect == EXPECT_TOPLEVEL)
28262857
current->expect = EXPECT_END;
28272858
else if (current->expect == EXPECT_OBJECT_VALUE)
@@ -2841,13 +2872,17 @@ int json_buildv(JsonVariant **ret, va_list ap) {
28412872

28422873
l = va_arg(ap, const char *);
28432874

2844-
if (!l)
2845-
add = JSON_VARIANT_MAGIC_NULL;
2846-
else {
2875+
if (l) {
2876+
/* Note that we don't care for current->n_suppress here, we should generate parsing
2877+
* errors even in suppressed object properties */
2878+
28472879
r = json_parse(l, &add, NULL, NULL);
28482880
if (r < 0)
28492881
goto finish;
2850-
}
2882+
} else
2883+
add = JSON_VARIANT_MAGIC_NULL;
2884+
2885+
n_subtract = 1;
28512886

28522887
if (current->expect == EXPECT_TOPLEVEL)
28532888
current->expect = EXPECT_END;
@@ -2881,6 +2916,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
28812916

28822917
stack[n_stack++] = (JsonStack) {
28832918
.expect = EXPECT_ARRAY_ELEMENT,
2919+
.n_suppress = current->n_suppress != 0 ? (size_t) -1 : 0, /* if we shall suppress the
2920+
* new array, then we should
2921+
* also suppress all array
2922+
* members */
28842923
};
28852924

28862925
break;
@@ -2893,9 +2932,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
28932932

28942933
assert(n_stack > 1);
28952934

2896-
r = json_variant_new_array(&add, current->elements, current->n_elements);
2897-
if (r < 0)
2898-
goto finish;
2935+
if (current->n_suppress == 0) {
2936+
r = json_variant_new_array(&add, current->elements, current->n_elements);
2937+
if (r < 0)
2938+
goto finish;
2939+
}
2940+
2941+
n_subtract = 1;
28992942

29002943
json_stack_release(current);
29012944
n_stack--, current--;
@@ -2912,9 +2955,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
29122955

29132956
l = va_arg(ap, char **);
29142957

2915-
r = json_variant_new_array_strv(&add, l);
2916-
if (r < 0)
2917-
goto finish;
2958+
if (current->n_suppress == 0) {
2959+
r = json_variant_new_array_strv(&add, l);
2960+
if (r < 0)
2961+
goto finish;
2962+
}
2963+
2964+
n_subtract = 1;
29182965

29192966
if (current->expect == EXPECT_TOPLEVEL)
29202967
current->expect = EXPECT_END;
@@ -2948,6 +2995,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
29482995

29492996
stack[n_stack++] = (JsonStack) {
29502997
.expect = EXPECT_OBJECT_KEY,
2998+
.n_suppress = current->n_suppress != 0 ? (size_t) -1 : 0, /* if we shall suppress the
2999+
* new object, then we should
3000+
* also suppress all object
3001+
* members */
29513002
};
29523003

29533004
break;
@@ -2961,9 +3012,13 @@ int json_buildv(JsonVariant **ret, va_list ap) {
29613012

29623013
assert(n_stack > 1);
29633014

2964-
r = json_variant_new_object(&add, current->elements, current->n_elements);
2965-
if (r < 0)
2966-
goto finish;
3015+
if (current->n_suppress == 0) {
3016+
r = json_variant_new_object(&add, current->elements, current->n_elements);
3017+
if (r < 0)
3018+
goto finish;
3019+
}
3020+
3021+
n_subtract = 1;
29673022

29683023
json_stack_release(current);
29693024
n_stack--, current--;
@@ -2980,22 +3035,64 @@ int json_buildv(JsonVariant **ret, va_list ap) {
29803035

29813036
n = va_arg(ap, const char *);
29823037

2983-
r = json_variant_new_string(&add, n);
2984-
if (r < 0)
3038+
if (current->n_suppress == 0) {
3039+
r = json_variant_new_string(&add, n);
3040+
if (r < 0)
3041+
goto finish;
3042+
}
3043+
3044+
n_subtract = 1;
3045+
3046+
current->expect = EXPECT_OBJECT_VALUE;
3047+
break;
3048+
}
3049+
3050+
case _JSON_BUILD_PAIR_CONDITION: {
3051+
const char *n;
3052+
bool b;
3053+
3054+
if (current->expect != EXPECT_OBJECT_KEY) {
3055+
r = -EINVAL;
29853056
goto finish;
3057+
}
3058+
3059+
b = va_arg(ap, int);
3060+
n = va_arg(ap, const char *);
3061+
3062+
if (b && current->n_suppress == 0) {
3063+
r = json_variant_new_string(&add, n);
3064+
if (r < 0)
3065+
goto finish;
3066+
}
3067+
3068+
n_subtract = 1; /* we generated one item */
3069+
3070+
if (!b && current->n_suppress != (size_t) -1)
3071+
current->n_suppress += 2; /* Suppress this one and the next item */
29863072

29873073
current->expect = EXPECT_OBJECT_VALUE;
29883074
break;
29893075
}}
29903076

2991-
if (add) {
3077+
/* If a variant was generated, add it to our current variant, but only if we are not supposed to suppress additions */
3078+
if (add && current->n_suppress == 0) {
29923079
if (!GREEDY_REALLOC(current->elements, current->n_elements_allocated, current->n_elements + 1)) {
29933080
r = -ENOMEM;
29943081
goto finish;
29953082
}
29963083

29973084
current->elements[current->n_elements++] = TAKE_PTR(add);
29983085
}
3086+
3087+
/* If we are supposed to suppress items, let's subtract how many items where generated from that
3088+
* counter. Except if the counter is (size_t) -1, i.e. we shall suppress an infinite number of elements
3089+
* on this stack level */
3090+
if (current->n_suppress != (size_t) -1) {
3091+
if (current->n_suppress <= n_subtract) /* Saturated */
3092+
current->n_suppress = 0;
3093+
else
3094+
current->n_suppress -= n_subtract;
3095+
}
29993096
}
30003097

30013098
done:

src/shared/json.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ enum {
177177
_JSON_BUILD_OBJECT_BEGIN,
178178
_JSON_BUILD_OBJECT_END,
179179
_JSON_BUILD_PAIR,
180+
_JSON_BUILD_PAIR_CONDITION,
180181
_JSON_BUILD_NULL,
181182
_JSON_BUILD_VARIANT,
182183
_JSON_BUILD_LITERAL,
@@ -192,6 +193,7 @@ enum {
192193
#define JSON_BUILD_ARRAY(...) _JSON_BUILD_ARRAY_BEGIN, __VA_ARGS__, _JSON_BUILD_ARRAY_END
193194
#define JSON_BUILD_OBJECT(...) _JSON_BUILD_OBJECT_BEGIN, __VA_ARGS__, _JSON_BUILD_OBJECT_END
194195
#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, ({ const char *_x = n; _x; }), __VA_ARGS__
196+
#define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, ({ bool _x = c; _x; }), ({ const char *_x = n; _x; }), __VA_ARGS__
195197
#define JSON_BUILD_NULL _JSON_BUILD_NULL
196198
#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, ({ JsonVariant *_x = v; _x; })
197199
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })

src/test/test-json.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,22 @@ static void test_build(void) {
316316

317317
a = json_variant_unref(a);
318318
b = json_variant_unref(b);
319+
320+
assert_se(json_build(&a, JSON_BUILD_OBJECT(
321+
JSON_BUILD_PAIR("x", JSON_BUILD_STRING("y")),
322+
JSON_BUILD_PAIR("z", JSON_BUILD_STRING("a")),
323+
JSON_BUILD_PAIR("b", JSON_BUILD_STRING("c"))
324+
)) >= 0);
325+
326+
assert_se(json_build(&b, JSON_BUILD_OBJECT(
327+
JSON_BUILD_PAIR("x", JSON_BUILD_STRING("y")),
328+
JSON_BUILD_PAIR_CONDITION(false, "p", JSON_BUILD_STRING("q")),
329+
JSON_BUILD_PAIR_CONDITION(true, "z", JSON_BUILD_STRING("a")),
330+
JSON_BUILD_PAIR_CONDITION(false, "j", JSON_BUILD_ARRAY(JSON_BUILD_STRING("k"), JSON_BUILD_STRING("u"), JSON_BUILD_STRING("i"))),
331+
JSON_BUILD_PAIR("b", JSON_BUILD_STRING("c"))
332+
)) >= 0);
333+
334+
assert_se(json_variant_equal(a, b));
319335
}
320336

321337
static void test_source(void) {

0 commit comments

Comments
 (0)