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,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&lt;/option&gt; @@ -3082,11 +3082,18 @@ outfile.pdf&lt;/option&gt;
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>&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 </para> 3097 </para>
3091 </listitem> 3098 </listitem>
3092 <listitem> 3099 <listitem>