Commit 72464041771de52ca6b3f8bc79aea84c23af30b6
1 parent
67d5ed3a
JSON: implement pattern keys in schema
Showing
4 changed files
with
51 additions
and
7 deletions
libqpdf/JSON.cc
| @@ -323,6 +323,7 @@ JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v, | @@ -323,6 +323,7 @@ JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v, | ||
| 323 | err_prefix = "json key \"" + prefix + "\""; | 323 | err_prefix = "json key \"" + prefix + "\""; |
| 324 | } | 324 | } |
| 325 | 325 | ||
| 326 | + std::string pattern_key; | ||
| 326 | if (sch_dict) | 327 | if (sch_dict) |
| 327 | { | 328 | { |
| 328 | if (! this_dict) | 329 | if (! this_dict) |
| @@ -331,6 +332,31 @@ JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v, | @@ -331,6 +332,31 @@ JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v, | ||
| 331 | errors.push_back(err_prefix + " is supposed to be a dictionary"); | 332 | errors.push_back(err_prefix + " is supposed to be a dictionary"); |
| 332 | return false; | 333 | return false; |
| 333 | } | 334 | } |
| 335 | + auto members = sch_dict->members; | ||
| 336 | + std::string key; | ||
| 337 | + if ((members.size() == 1) && | ||
| 338 | + ((key = members.begin()->first, key.length() > 2) && | ||
| 339 | + (key.at(0) == '<') && | ||
| 340 | + (key.at(key.length() - 1) == '>'))) | ||
| 341 | + { | ||
| 342 | + pattern_key = key; | ||
| 343 | + } | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + if (sch_dict && (! pattern_key.empty())) | ||
| 347 | + { | ||
| 348 | + auto pattern_schema = sch_dict->members[pattern_key].getPointer(); | ||
| 349 | + for (auto iter: this_dict->members) | ||
| 350 | + { | ||
| 351 | + std::string const& key = iter.first; | ||
| 352 | + checkSchemaInternal( | ||
| 353 | + this_dict->members[key].getPointer(), | ||
| 354 | + pattern_schema, | ||
| 355 | + errors, prefix + "." + key); | ||
| 356 | + } | ||
| 357 | + } | ||
| 358 | + else if (sch_dict) | ||
| 359 | + { | ||
| 334 | for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter = | 360 | for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter = |
| 335 | sch_dict->members.begin(); | 361 | sch_dict->members.begin(); |
| 336 | iter != sch_dict->members.end(); ++iter) | 362 | iter != sch_dict->members.end(); ++iter) |
libtests/json.cc
| @@ -112,6 +112,11 @@ static void test_schema() | @@ -112,6 +112,11 @@ static void test_schema() | ||
| 112 | " >>" | 112 | " >>" |
| 113 | " ]" | 113 | " ]" |
| 114 | ">>").getJSON(); | 114 | ">>").getJSON(); |
| 115 | + JSON three = JSON::makeDictionary(); | ||
| 116 | + three.addDictionaryMember( | ||
| 117 | + "<objid>", | ||
| 118 | + QPDFObjectHandle::parse("<< /z (ebra) >>").getJSON()); | ||
| 119 | + schema.addDictionaryMember("/three", three); | ||
| 115 | JSON a = QPDFObjectHandle::parse("[(not a) (dictionary)]").getJSON(); | 120 | JSON a = QPDFObjectHandle::parse("[(not a) (dictionary)]").getJSON(); |
| 116 | check_schema(a, schema, false, "top-level type mismatch"); | 121 | check_schema(a, schema, false, "top-level type mismatch"); |
| 117 | JSON b = QPDFObjectHandle::parse( | 122 | JSON b = QPDFObjectHandle::parse( |
| @@ -142,8 +147,12 @@ static void test_schema() | @@ -142,8 +147,12 @@ static void test_schema() | ||
| 142 | " /glarp (4 enspliel)" | 147 | " /glarp (4 enspliel)" |
| 143 | " >>" | 148 | " >>" |
| 144 | " ]" | 149 | " ]" |
| 150 | + " /three <<" | ||
| 151 | + " /anything << /x (oops) >>" | ||
| 152 | + " /else << /z (okay) >>" | ||
| 153 | + " >>" | ||
| 145 | ">>").getJSON(); | 154 | ">>").getJSON(); |
| 146 | - check_schema(b, schema, false, "top-level type mismatch"); | 155 | + check_schema(b, schema, false, "missing items"); |
| 147 | check_schema(a, a, false, "top-level schema array error"); | 156 | check_schema(a, a, false, "top-level schema array error"); |
| 148 | check_schema(b, b, false, "lower-level schema array error"); | 157 | check_schema(b, b, false, "lower-level schema array error"); |
| 149 | check_schema(schema, schema, true, "pass"); | 158 | check_schema(schema, schema, true, "pass"); |
libtests/qtest/json/json.out
| 1 | --- top-level type mismatch | 1 | --- top-level type mismatch |
| 2 | top-level object is supposed to be a dictionary | 2 | top-level object is supposed to be a dictionary |
| 3 | --- | 3 | --- |
| 4 | ---- top-level type mismatch | 4 | +--- missing items |
| 5 | json key "./one./a": key "/q" is present in schema but missing in object | 5 | json key "./one./a": key "/q" is present in schema but missing in object |
| 6 | json key "./one./a./r" is supposed to be a dictionary | 6 | json key "./one./a./r" is supposed to be a dictionary |
| 7 | json key "./one./a./s" is supposed to be an array | 7 | json key "./one./a./s" is supposed to be an array |
| 8 | json key "./one./a": key "/t" is not present in schema but appears in object | 8 | json key "./one./a": key "/t" is not present in schema but appears in object |
| 9 | +json key "./three./anything": key "/z" is present in schema but missing in object | ||
| 10 | +json key "./three./anything": key "/x" is not present in schema but appears in object | ||
| 9 | json key "./two.1": key "/glarp" is present in schema but missing in object | 11 | json key "./two.1": key "/glarp" is present in schema but missing in object |
| 10 | json key "./two.1": key "/flarp" is not present in schema but appears in object | 12 | json key "./two.1": key "/flarp" is not present in schema but appears in object |
| 11 | json key "./two.2" is supposed to be a dictionary | 13 | json key "./two.2" is supposed to be a dictionary |
manual/qpdf-manual.xml
| @@ -3082,11 +3082,18 @@ outfile.pdf</option> | @@ -3082,11 +3082,18 @@ outfile.pdf</option> | ||
| 3082 | <itemizedlist> | 3082 | <itemizedlist> |
| 3083 | <listitem> | 3083 | <listitem> |
| 3084 | <para> | 3084 | <para> |
| 3085 | - A dictionary in the help output means that the corresponding | ||
| 3086 | - location in the actual JSON output is also a dictionary with | ||
| 3087 | - exactly the same keys; that is, no keys present in help are | ||
| 3088 | - absent in the real output, and no keys will be present in | ||
| 3089 | - the real output that are not in help. | 3085 | + A dictionary in the help output means that the |
| 3086 | + corresponding location in the actual JSON output is also a | ||
| 3087 | + dictionary with exactly the same keys; that is, no keys | ||
| 3088 | + present in help are absent in the real output, and no keys | ||
| 3089 | + will be present in the real output that are not in help. As | ||
| 3090 | + a special case, if the dictionary has a single key whose | ||
| 3091 | + name starts with <literal><</literal> and ends with | ||
| 3092 | + <literal>></literal>, it means that the JSON output is a | ||
| 3093 | + dictionary that can have any keys, each of which conforms | ||
| 3094 | + to the value of the special key. This is used for cases in | ||
| 3095 | + which the keys of the dictionary are things like object | ||
| 3096 | + IDs. | ||
| 3090 | </para> | 3097 | </para> |
| 3091 | </listitem> | 3098 | </listitem> |
| 3092 | <listitem> | 3099 | <listitem> |