JSON schema -- accept single item in place of array

When the schema wants a variable-length array, allow a single item as
well as allowing an array.
This commit is contained in:
Jay Berkenbilt 2022-07-24 16:17:03 -04:00
parent b3e6d445cb
commit f8d1ab9462
7 changed files with 46 additions and 28 deletions

View File

@ -1,5 +1,15 @@
2022-07-24 Jay Berkenbilt <ejb@ql.org>
* include/qpdf/JSON.hh: Schema validation: allow a single item to
appear anywhere that the schema has an array of a single item.
This makes it possible to change an element of the schema from an
item to an array to allow the data to accept an array where a
single value was previously required. This change is needed to
allow QPDFJob JSON to start accepting multiple items where a
single item used to be expected without breaking backward
compatibility. Without this change, the earlier fix to
removeAttachment would be a breaking change.
* QPDFObjectHandle: for the methods insertItem, appendItem,
eraseItem, replaceKey, and removeKey, add a corresponding
"AndGetNew" and/or "AndGetOld" methods. The ones that end with

4
TODO
View File

@ -19,10 +19,6 @@ Pending changes:
appimage build specifically is setting the runpath, which is
actually desirable in this case. Make sure to understand and
document this. Maybe add a check for it in the build.
* Make job JSON accept a single element and treat as an array of one
when an array is expected. This allows for making things repeatable
in the future without breaking compatibility and is needed for the
remote-attachment fix to be backward-compatible.
* Decide what to do about #664 (get*Box)
* Add an option --ignore-encryption to ignore encryption information
and treat encrypted files as if they weren't encrypted. This should

View File

@ -184,6 +184,14 @@ class JSON
// * The schema is a nested structure containing dictionaries,
// single-element arrays, and strings only.
// * Recursively walk the schema.
// * Whenever the schema has an array of length 1 and the object
// does not have an array in the corresponding location,
// validate the object against the array's single element.
// This effectively enables a single element to appear in
// place of an array and be treated as if it were an array of
// one element. This makes it possible to decide later that
// something that used to contain a single element now allows
// an array without invalidating any old data.
// * If the current value is a dictionary, this object must have
// a dictionary in the same place with the same keys. If flags
// contains f_optional, a key in the schema does not have to

View File

@ -538,26 +538,27 @@ JSON::checkSchemaInternal(
}
}
} else if (sch_arr) {
if (!this_arr) {
QTC::TC("libtests", "JSON wanted array");
errors.push_back(err_prefix + " is supposed to be an array");
return false;
}
if (sch_arr->elements.size() != 1) {
QTC::TC("libtests", "JSON schema array error");
errors.push_back(
err_prefix + " schema array contains other than one item");
return false;
}
int i = 0;
for (auto const& element: this_arr->elements) {
if (this_arr) {
int i = 0;
for (auto const& element: this_arr->elements) {
checkSchemaInternal(
element.get(),
sch_arr->elements.at(0).get(),
flags,
errors,
prefix + "." + QUtil::int_to_string(i));
++i;
}
} else {
QTC::TC("libtests", "JSON schema array for single item");
checkSchemaInternal(
element.get(),
sch_arr->elements.at(0).get(),
flags,
errors,
prefix + "." + QUtil::int_to_string(i));
++i;
this_v, sch_arr->elements.at(0).get(), flags, errors, prefix);
}
} else if (!sch_str) {
QTC::TC("libtests", "JSON schema other type");

View File

@ -162,7 +162,9 @@ test_schema()
"x": "ecks"
},
"s": [
"esses"
{
"ss": "esses"
}
]
}
},
@ -236,6 +238,8 @@ test_schema()
JSON bad_schema = JSON::parse(R"({"a": true, "b": "potato?"})");
check_schema(bad_schema, bad_schema, 0, false, "bad schema field type");
// "two" exercises the case of the JSON containing a single
// element where the schema has an array.
JSON good = JSON::parse(R"(
{
"one": {
@ -245,17 +249,15 @@ test_schema()
"x": [1, null]
},
"s": [
null,
"anything"
{"ss": null},
{"ss": "anything"}
]
}
},
"two": [
{
"glarp": "enspliel",
"goose": 3.14
}
],
"two": {
"glarp": "enspliel",
"goose": 3.14
},
"three": {
"<objid>": {
"z": "ebra"

View File

@ -36,7 +36,6 @@ Pl_PNGFilter decodePaeth 0
Pl_TIFFPredictor processRow 1
JSON wanted dictionary 0
JSON key missing in object 0
JSON wanted array 0
JSON schema array error 0
JSON key extra in object 0
QPDFArgParser read args from stdin 0
@ -93,3 +92,4 @@ JSON 16 high high 0
JSON 16 low not after high 0
JSON 16 dangling high 0
JSON parse duplicate key 0
JSON schema array for single item 0

View File

@ -4,7 +4,8 @@ top-level object is supposed to be a dictionary
--- missing items
json key ".one.a": key "q" is present in schema but missing in object
json key ".one.a.r" is supposed to be a dictionary
json key ".one.a.s" is supposed to be an array
json key ".one.a.s": key "ss" is present in schema but missing in object
json key ".one.a.s": key "z" is not present in schema but appears in object
json key ".one.a": key "t" is not present in schema but appears in object
json key ".three.anything": key "z" is present in schema but missing in object
json key ".three.anything": key "x" is not present in schema but appears in object