sd-json: make sure all dispatch helpers do something sensible in case of "null" JSON value

Most of our dispatch helpers already do something useful in case they
are invoked on a null JSON value: they translate this to the appropriate
niche value for the type, if there is one.

Add the same for *all* dispatchers we have, to make this fully
systematic.

For various types it's not always clear which niche value to pick. I
opted for UINT{8,16,32,64}_MAX for the various unsigned integers, which
maps our own use in most cases. I opted for -1 for the various signed
integer types. For arrays/blobs of stuff I opted for the empty
array/blob, and for booleans I opted for false.

Of course, in various cases this is not going to be the right niche
value, but that's entirely fine, after all before a json value reaches a
dispatcher function it must pass one of two type checks first:

1. Either the .type field of sd_json_dispatch_field must be
   _SD_JSON_VARIANT_TYPE_INVALID to not do a type check at all

2. Or the .type field is set, but then the SD_JSON_NULLABLE flag must be
   set in .flags.

This means, accidentally generating the niche values on null is not
really likely.
This commit is contained in:
Lennart Poettering
2025-10-24 09:57:48 +02:00
parent 3cc3b469cc
commit e01f8990f1
2 changed files with 80 additions and 0 deletions

View File

@@ -31,6 +31,11 @@ int json_dispatch_unhex_iovec(const char *name, sd_json_variant *variant, sd_jso
size_t sz;
int r;
if (sd_json_variant_is_null(variant)) {
iovec_done(iov);
return 0;
}
if (!sd_json_variant_is_string(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
@@ -49,6 +54,11 @@ int json_dispatch_unbase64_iovec(const char *name, sd_json_variant *variant, sd_
size_t sz;
int r;
if (sd_json_variant_is_null(variant)) {
iovec_done(iov);
return 0;
}
if (!sd_json_variant_is_string(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
@@ -68,6 +78,11 @@ int json_dispatch_byte_array_iovec(const char *name, sd_json_variant *variant, s
assert(variant);
if (sd_json_variant_is_null(variant)) {
iovec_done(iov);
return 0;
}
if (!sd_json_variant_is_array(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
@@ -169,6 +184,11 @@ int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_di
_cleanup_(iovec_done) struct iovec iov = {};
int r;
if (sd_json_variant_is_null(variant)) {
*address = (struct in_addr) {};
return 0;
}
r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
if (r < 0)
return r;

View File

@@ -5292,6 +5292,11 @@ _public_ int sd_json_dispatch_stdbool(const char *name, sd_json_variant *variant
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*b = false;
return 0;
}
if (!sd_json_variant_is_boolean(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name));
@@ -5305,6 +5310,11 @@ _public_ int sd_json_dispatch_intbool(const char *name, sd_json_variant *variant
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*b = false;
return 0;
}
if (!sd_json_variant_is_boolean(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name));
@@ -5336,6 +5346,11 @@ _public_ int sd_json_dispatch_int64(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
/* Also accept numbers formatted as string, to increase compatibility with less capable JSON
* implementations that cannot do 64bit integers. */
if (sd_json_variant_is_string(variant) && safe_atoi64(sd_json_variant_string(variant), i) >= 0)
@@ -5354,6 +5369,11 @@ _public_ int sd_json_dispatch_uint64(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT64_MAX;
return 0;
}
/* Since 64bit values (in particular unsigned ones) in JSON are problematic, let's also accept them
* formatted as strings. If this is not desired make sure to set the .type field in
* sd_json_dispatch_field to SD_JSON_UNSIGNED rather than _SD_JSON_VARIANT_TYPE_INVALID, so that
@@ -5377,6 +5397,11 @@ _public_ int sd_json_dispatch_uint32(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT32_MAX;
return 0;
}
r = sd_json_dispatch_uint64(name, variant, flags, &u64);
if (r < 0)
return r;
@@ -5399,6 +5424,11 @@ _public_ int sd_json_dispatch_int32(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
r = sd_json_dispatch_int64(name, variant, flags, &i64);
if (r < 0)
return r;
@@ -5421,6 +5451,11 @@ _public_ int sd_json_dispatch_int16(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
r = sd_json_dispatch_int64(name, variant, flags, &i64);
if (r < 0)
return r;
@@ -5440,6 +5475,11 @@ _public_ int sd_json_dispatch_uint16(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT16_MAX;
return 0;
}
r = sd_json_dispatch_uint64(name, variant, flags, &u64);
if (r < 0)
return r;
@@ -5459,6 +5499,11 @@ _public_ int sd_json_dispatch_int8(const char *name, sd_json_variant *variant, s
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
r = sd_json_dispatch_int64(name, variant, flags, &i64);
if (r < 0)
return r;
@@ -5478,6 +5523,11 @@ _public_ int sd_json_dispatch_uint8(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT8_MAX;
return 0;
}
r = sd_json_dispatch_uint64(name, variant, flags, &u64);
if (r < 0)
return r;
@@ -5495,6 +5545,11 @@ _public_ int sd_json_dispatch_double(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*d = NAN;
return 0;
}
/* Note, this will take care of parsing NaN, -Infinity, Infinity for us */
if (sd_json_variant_is_string(variant) && safe_atod(sd_json_variant_string(variant), d) >= 0)
return 0;
@@ -5514,6 +5569,11 @@ _public_ int sd_json_dispatch_string(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*s = mfree(*s);
return 0;
}
r = sd_json_dispatch_const_string(name, variant, flags, &n);
if (r < 0)
return r;