fourth, etc. format character represent a value. Any value may be
an object or array, i.e. recursive value building is supported.
+Whitespace, ``:`` and ``,`` are ignored.
+
The following functions compose the value building API:
.. function:: json_t *json_pack(const char *fmt, ...)
``fmt`` may contain objects and arrays as values, i.e. recursive
value extraction is supporetd.
+ .. versionadded:: 2.3
+ Any ``s`` representing a key may be suffixed with a ``?`` to
+ make the key optional. If the key is not found, nothing is
+ extracted. See below for an example.
+
``!``
This special format character is used to enable the check that
all object and array items are accessed, on a per-value basis. It
or object as the last format character before the closing bracket
or brace.
+Whitespace, ``:`` and ``,`` are ignored.
+
The following functions compose the parsing and validation API:
.. function:: int json_unpack(json_t *root, const char *fmt, ...)
json_unpack(root, "[ii!]", &myint1, &myint2);
/* returns -1 for failed validation */
+ /* root is an empty JSON object */
+ int myint = 0, myint2 = 0;
+ json_unpack(root, "{s?i, s?[ii]}",
+ "foo", &myint1,
+ "bar", &myint2, &myint3);
+ /* myint1, myint2 or myint3 is no touched as "foo" and "bar" don't exist */
+
Equality
========
return -1;
}
- if(!json_is_object(root)) {
+ if(root && !json_is_object(root)) {
set_error(s, "<validation>", "Expected object, got %s",
type_name(root));
goto out;
while(s->token != '}') {
const char *key;
json_t *value;
+ int opt = 0;
if(strict != 0) {
set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
next_token(s);
- value = json_object_get(root, key);
- if(!value) {
- set_error(s, "<validation>", "Object item not found: %s", key);
- goto out;
+ if(s->token == '?') {
+ opt = 1;
+ next_token(s);
+ }
+
+ if(!root) {
+ /* skipping */
+ value = NULL;
+ }
+ else {
+ value = json_object_get(root, key);
+ if(!value && !opt) {
+ set_error(s, "<validation>", "Object item not found: %s", key);
+ goto out;
+ }
}
if(unpack(s, value, ap))
if(strict == 0 && (s->flags & JSON_STRICT))
strict = 1;
- if(strict == 1 && key_set.size != json_object_size(root)) {
+ if(root && strict == 1 && key_set.size != json_object_size(root)) {
long diff = (long)json_object_size(root) - (long)key_set.size;
set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
goto out;
size_t i = 0;
int strict = 0;
- if(!json_is_array(root)) {
+ if(root && !json_is_array(root)) {
set_error(s, "<validation>", "Expected array, got %s", type_name(root));
return -1;
}
return -1;
}
- value = json_array_get(root, i);
- if(!value) {
- set_error(s, "<validation>", "Array index %lu out of range",
- (unsigned long)i);
- return -1;
+ if(!root) {
+ /* skipping */
+ value = NULL;
+ }
+ else {
+ value = json_array_get(root, i);
+ if(!value) {
+ set_error(s, "<validation>", "Array index %lu out of range",
+ (unsigned long)i);
+ return -1;
+ }
}
if(unpack(s, value, ap))
if(strict == 0 && (s->flags & JSON_STRICT))
strict = 1;
- if(strict == 1 && i != json_array_size(root)) {
+ if(root && strict == 1 && i != json_array_size(root)) {
long diff = (long)json_array_size(root) - (long)i;
set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
return -1;
return unpack_array(s, root, ap);
case 's':
- if(!json_is_string(root)) {
+ if(root && !json_is_string(root)) {
set_error(s, "<validation>", "Expected string, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
- const char **str;
+ const char **target;
- str = va_arg(*ap, const char **);
- if(!str) {
+ target = va_arg(*ap, const char **);
+ if(!target) {
set_error(s, "<args>", "NULL string argument");
return -1;
}
- *str = json_string_value(root);
+ if(root)
+ *target = json_string_value(root);
}
return 0;
case 'i':
- if(!json_is_integer(root)) {
+ if(root && !json_is_integer(root)) {
set_error(s, "<validation>", "Expected integer, got %s",
type_name(root));
return -1;
}
- if(!(s->flags & JSON_VALIDATE_ONLY))
- *va_arg(*ap, int*) = json_integer_value(root);
+ if(!(s->flags & JSON_VALIDATE_ONLY)) {
+ int *target = va_arg(*ap, int*);
+ if(root)
+ *target = json_integer_value(root);
+ }
return 0;
case 'I':
- if(!json_is_integer(root)) {
+ if(root && !json_is_integer(root)) {
set_error(s, "<validation>", "Expected integer, got %s",
type_name(root));
return -1;
}
- if(!(s->flags & JSON_VALIDATE_ONLY))
- *va_arg(*ap, json_int_t*) = json_integer_value(root);
+ if(!(s->flags & JSON_VALIDATE_ONLY)) {
+ json_int_t *target = va_arg(*ap, json_int_t*);
+ if(root)
+ *target = json_integer_value(root);
+ }
return 0;
case 'b':
- if(!json_is_boolean(root)) {
+ if(root && !json_is_boolean(root)) {
set_error(s, "<validation>", "Expected true or false, got %s",
type_name(root));
return -1;
}
- if(!(s->flags & JSON_VALIDATE_ONLY))
- *va_arg(*ap, int*) = json_is_true(root);
+ if(!(s->flags & JSON_VALIDATE_ONLY)) {
+ int *target = va_arg(*ap, int*);
+ if(root)
+ *target = json_is_true(root);
+ }
return 0;
case 'f':
- if(!json_is_real(root)) {
+ if(root && !json_is_real(root)) {
set_error(s, "<validation>", "Expected real, got %s",
type_name(root));
return -1;
}
- if(!(s->flags & JSON_VALIDATE_ONLY))
- *va_arg(*ap, double*) = json_real_value(root);
+ if(!(s->flags & JSON_VALIDATE_ONLY)) {
+ double *target = va_arg(*ap, double*);
+ if(root)
+ *target = json_real_value(root);
+ }
return 0;
case 'F':
- if(!json_is_number(root)) {
+ if(root && !json_is_number(root)) {
set_error(s, "<validation>", "Expected real or integer, got %s",
type_name(root));
return -1;
}
- if(!(s->flags & JSON_VALIDATE_ONLY))
- *va_arg(*ap, double*) = json_number_value(root);
+ if(!(s->flags & JSON_VALIDATE_ONLY)) {
+ double *target = va_arg(*ap, double*);
+ if(root)
+ *target = json_number_value(root);
+ }
return 0;
case 'O':
- if(!(s->flags & JSON_VALIDATE_ONLY))
+ if(root && !(s->flags & JSON_VALIDATE_ONLY))
json_incref(root);
/* Fall through */
case 'o':
- if(!(s->flags & JSON_VALIDATE_ONLY))
- *va_arg(*ap, json_t**) = root;
+ if(!(s->flags & JSON_VALIDATE_ONLY)) {
+ json_t **target = va_arg(*ap, json_t**);
+ if(root)
+ *target = root;
+ }
return 0;
case 'n':
/* Never assign, just validate */
- if(!json_is_null(root)) {
+ if(root && !json_is_null(root)) {
set_error(s, "<validation>", "Expected null, got %s",
type_name(root));
return -1;
fail("json_unpack nested array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
+
+ /* Optional values */
+ j = json_object();
+ i1 = 0;
+ if(json_unpack(j, "{s?i}", "foo", &i1))
+ fail("json_unpack failed for optional key");
+ if(i1 != 0)
+ fail("json_unpack unpacked an optional key");
+ json_decref(j);
+
+ i1 = 0;
+ j = json_pack("{si}", "foo", 42);
+ if(json_unpack(j, "{s?i}", "foo", &i1))
+ fail("json_unpack failed for an optional value");
+ if(i1 != 42)
+ fail("json_unpack failed to unpack an optional value");
+ json_decref(j);
+
+ j = json_object();
+ i1 = i2 = i3 = 0;
+ if(json_unpack(j, "{s?[ii]s?{s{si}}}",
+ "foo", &i1, &i2,
+ "bar", "baz", "quux", &i3))
+ fail("json_unpack failed for complex optional values");
+ if(i1 != 0 || i2 != 0 || i3 != 0)
+ fail("json_unpack unexpectedly unpacked something");
+ json_decref(j);
+
+ j = json_pack("{s{si}}", "foo", "bar", 42);
+ if(json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1))
+ fail("json_unpack failed for complex optional values");
+ if(i1 != 42)
+ fail("json_unpack failed to unpack");
+ json_decref(j);
}