assert(variant);
+ /* Also accept numbers formatted as string, to increase compatibility with less capable JSON
+ * implementations that cannot do 64bit integers. */
+ if (json_variant_is_string(variant) && safe_atoi64(json_variant_string(variant), i) >= 0)
+ return 0;
+
if (!json_variant_is_integer(variant))
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer, nor one formatted as decimal string.", strna(name));
*i = json_variant_integer(variant);
return 0;
assert(variant);
+ /* 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 JsonDispatch to
+ * JSON_UNSIGNED rather than _JSON_VARIANT_TYPE_INVALID, so that json_dispatch() already filters out
+ * the non-matching type. */
+
+ if (json_variant_is_string(variant) && safe_atou64(json_variant_string(variant), u) >= 0)
+ return 0;
+
if (!json_variant_is_unsigned(variant))
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer.", strna(name));
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer, nor one formatted as decimal string.", strna(name));
*u = json_variant_unsigned(variant);
return 0;
int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
uint32_t *u = ASSERT_PTR(userdata);
+ uint64_t u64;
+ int r;
assert(variant);
- if (!json_variant_is_unsigned(variant))
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer.", strna(name));
+ r = json_dispatch_uint64(name, variant, flags, &u64);
+ if (r < 0)
+ return r;
- if (json_variant_unsigned(variant) > UINT32_MAX)
+ if (u64 > UINT32_MAX)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
- *u = (uint32_t) json_variant_unsigned(variant);
+ *u = (uint32_t) u64;
return 0;
}
int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
int32_t *i = ASSERT_PTR(userdata);
+ int64_t i64;
+ int r;
assert(variant);
- if (!json_variant_is_integer(variant))
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
+ r = json_dispatch_int64(name, variant, flags, &i64);
+ if (r < 0)
+ return r;
- if (json_variant_integer(variant) < INT32_MIN || json_variant_integer(variant) > INT32_MAX)
+ if (i64 < INT32_MIN || i64 > INT32_MAX)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
- *i = (int32_t) json_variant_integer(variant);
+ *i = (int32_t) i64;
return 0;
}
int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
int16_t *i = ASSERT_PTR(userdata);
+ int64_t i64;
+ int r;
assert(variant);
- if (!json_variant_is_integer(variant))
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
+ r = json_dispatch_int64(name, variant, flags, &i64);
+ if (r < 0)
+ return r;
- if (json_variant_integer(variant) < INT16_MIN || json_variant_integer(variant) > INT16_MAX)
+ if (i64 < INT16_MIN || i64 > INT16_MAX)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
- *i = (int16_t) json_variant_integer(variant);
+ *i = (int16_t) i64;
return 0;
}
int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
- uint16_t *i = ASSERT_PTR(userdata);
+ uint16_t *u = ASSERT_PTR(userdata);
+ uint64_t u64;
+ int r;
assert(variant);
- if (!json_variant_is_unsigned(variant))
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer.", strna(name));
+ r = json_dispatch_uint64(name, variant, flags, &u64);
+ if (r < 0)
+ return r;
- if (json_variant_unsigned(variant) > UINT16_MAX)
+ if (u64 > UINT16_MAX)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
- *i = (uint16_t) json_variant_unsigned(variant);
+ *u = (uint16_t) u64;
return 0;
}
assert_se(json_variant_equal(s, nd));
}
+TEST(json_dispatch) {
+ struct foobar {
+ uint64_t a, b;
+ int64_t c, d;
+ uint32_t e, f;
+ int32_t g, h;
+ uint16_t i, j;
+ int16_t k, l;
+ } foobar = {};
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ assert_se(json_build(&v, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("a", JSON_BUILD_UNSIGNED(UINT64_MAX)),
+ JSON_BUILD_PAIR("b", JSON_BUILD_STRING("18446744073709551615")),
+ JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+ JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+ JSON_BUILD_PAIR("e", JSON_BUILD_UNSIGNED(UINT32_MAX)),
+ JSON_BUILD_PAIR("f", JSON_BUILD_STRING("4294967295")),
+ JSON_BUILD_PAIR("g", JSON_BUILD_INTEGER(INT32_MIN)),
+ JSON_BUILD_PAIR("h", JSON_BUILD_STRING("-2147483648")),
+ JSON_BUILD_PAIR("i", JSON_BUILD_UNSIGNED(UINT16_MAX)),
+ JSON_BUILD_PAIR("j", JSON_BUILD_STRING("65535")),
+ JSON_BUILD_PAIR("k", JSON_BUILD_INTEGER(INT16_MIN)),
+ JSON_BUILD_PAIR("l", JSON_BUILD_STRING("-32768")))) >= 0);
+
+ assert_se(json_variant_dump(v, JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO, stdout, /* prefix= */ NULL) >= 0);
+
+ JsonDispatch table[] = {
+ { "a", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct foobar, a) },
+ { "b", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct foobar, b) },
+ { "c", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int64, offsetof(struct foobar, c) },
+ { "d", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int64, offsetof(struct foobar, d) },
+ { "e", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32, offsetof(struct foobar, e) },
+ { "f", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32, offsetof(struct foobar, f) },
+ { "g", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int32, offsetof(struct foobar, g) },
+ { "h", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int32, offsetof(struct foobar, h) },
+ { "i", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint16, offsetof(struct foobar, i) },
+ { "j", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint16, offsetof(struct foobar, j) },
+ { "k", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int16, offsetof(struct foobar, k) },
+ { "l", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int16, offsetof(struct foobar, l) },
+ {}
+ };
+
+ assert_se(json_dispatch(v, table, JSON_LOG, &foobar) >= 0);
+
+ assert_se(foobar.a == UINT64_MAX);
+ assert_se(foobar.b == UINT64_MAX);
+ assert_se(foobar.c == INT64_MIN);
+ assert_se(foobar.d == INT64_MIN);
+
+ assert_se(foobar.e == UINT32_MAX);
+ assert_se(foobar.f == UINT32_MAX);
+ assert_se(foobar.g == INT32_MIN);
+ assert_se(foobar.h == INT32_MIN);
+
+ assert_se(foobar.i == UINT16_MAX);
+ assert_se(foobar.j == UINT16_MAX);
+ assert_se(foobar.k == INT16_MIN);
+ assert_se(foobar.l == INT16_MIN);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);