Commit 72464041771de52ca6b3f8bc79aea84c23af30b6

Authored by Jay Berkenbilt
1 parent 67d5ed3a

JSON: implement pattern keys in schema

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&lt;/option&gt;
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>&lt;</literal> and ends with
  3092 + <literal>&gt;</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>
... ...