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 | 323 | err_prefix = "json key \"" + prefix + "\""; |
| 324 | 324 | } |
| 325 | 325 | |
| 326 | + std::string pattern_key; | |
| 326 | 327 | if (sch_dict) |
| 327 | 328 | { |
| 328 | 329 | if (! this_dict) |
| ... | ... | @@ -331,6 +332,31 @@ JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v, |
| 331 | 332 | errors.push_back(err_prefix + " is supposed to be a dictionary"); |
| 332 | 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 | 360 | for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter = |
| 335 | 361 | sch_dict->members.begin(); |
| 336 | 362 | iter != sch_dict->members.end(); ++iter) | ... | ... |
libtests/json.cc
| ... | ... | @@ -112,6 +112,11 @@ static void test_schema() |
| 112 | 112 | " >>" |
| 113 | 113 | " ]" |
| 114 | 114 | ">>").getJSON(); |
| 115 | + JSON three = JSON::makeDictionary(); | |
| 116 | + three.addDictionaryMember( | |
| 117 | + "<objid>", | |
| 118 | + QPDFObjectHandle::parse("<< /z (ebra) >>").getJSON()); | |
| 119 | + schema.addDictionaryMember("/three", three); | |
| 115 | 120 | JSON a = QPDFObjectHandle::parse("[(not a) (dictionary)]").getJSON(); |
| 116 | 121 | check_schema(a, schema, false, "top-level type mismatch"); |
| 117 | 122 | JSON b = QPDFObjectHandle::parse( |
| ... | ... | @@ -142,8 +147,12 @@ static void test_schema() |
| 142 | 147 | " /glarp (4 enspliel)" |
| 143 | 148 | " >>" |
| 144 | 149 | " ]" |
| 150 | + " /three <<" | |
| 151 | + " /anything << /x (oops) >>" | |
| 152 | + " /else << /z (okay) >>" | |
| 153 | + " >>" | |
| 145 | 154 | ">>").getJSON(); |
| 146 | - check_schema(b, schema, false, "top-level type mismatch"); | |
| 155 | + check_schema(b, schema, false, "missing items"); | |
| 147 | 156 | check_schema(a, a, false, "top-level schema array error"); |
| 148 | 157 | check_schema(b, b, false, "lower-level schema array error"); |
| 149 | 158 | check_schema(schema, schema, true, "pass"); | ... | ... |
libtests/qtest/json/json.out
| 1 | 1 | --- top-level type mismatch |
| 2 | 2 | top-level object is supposed to be a dictionary |
| 3 | 3 | --- |
| 4 | ---- top-level type mismatch | |
| 4 | +--- missing items | |
| 5 | 5 | json key "./one./a": key "/q" is present in schema but missing in object |
| 6 | 6 | json key "./one./a./r" is supposed to be a dictionary |
| 7 | 7 | json key "./one./a./s" is supposed to be an array |
| 8 | 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 | 11 | json key "./two.1": key "/glarp" is present in schema but missing in object |
| 10 | 12 | json key "./two.1": key "/flarp" is not present in schema but appears in object |
| 11 | 13 | json key "./two.2" is supposed to be a dictionary | ... | ... |
manual/qpdf-manual.xml
| ... | ... | @@ -3082,11 +3082,18 @@ outfile.pdf</option> |
| 3082 | 3082 | <itemizedlist> |
| 3083 | 3083 | <listitem> |
| 3084 | 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 | 3097 | </para> |
| 3091 | 3098 | </listitem> |
| 3092 | 3099 | <listitem> | ... | ... |