Commit 7be985b032d959fd9763088e6b36c7a923895b4e
Committed by
GitHub
Merge pull request #1121 from m-holger/dj
Handle default-constructed JSON objects
Showing
2 changed files
with
95 additions
and
47 deletions
libqpdf/JSON.cc
| @@ -191,7 +191,7 @@ JSON::JSON_blob::write(Pipeline* p, size_t) const | @@ -191,7 +191,7 @@ JSON::JSON_blob::write(Pipeline* p, size_t) const | ||
| 191 | void | 191 | void |
| 192 | JSON::write(Pipeline* p, size_t depth) const | 192 | JSON::write(Pipeline* p, size_t depth) const |
| 193 | { | 193 | { |
| 194 | - if (nullptr == m->value) { | 194 | + if (!m) { |
| 195 | *p << "null"; | 195 | *p << "null"; |
| 196 | } else { | 196 | } else { |
| 197 | m->value->write(p, depth); | 197 | m->value->write(p, depth); |
| @@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const | @@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const | ||
| 201 | std::string | 201 | std::string |
| 202 | JSON::unparse() const | 202 | JSON::unparse() const |
| 203 | { | 203 | { |
| 204 | + if (!m) { | ||
| 205 | + return "null"; | ||
| 206 | + } | ||
| 204 | std::string s; | 207 | std::string s; |
| 205 | Pl_String p("unparse", nullptr, s); | 208 | Pl_String p("unparse", nullptr, s); |
| 206 | write(&p, 0); | 209 | write(&p, 0); |
| @@ -275,8 +278,8 @@ JSON::makeDictionary() | @@ -275,8 +278,8 @@ JSON::makeDictionary() | ||
| 275 | JSON | 278 | JSON |
| 276 | JSON::addDictionaryMember(std::string const& key, JSON const& val) | 279 | JSON::addDictionaryMember(std::string const& key, JSON const& val) |
| 277 | { | 280 | { |
| 278 | - if (auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get())) { | ||
| 279 | - return obj->members[encode_string(key)] = val.m->value ? val : makeNull(); | 281 | + if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) { |
| 282 | + return obj->members[encode_string(key)] = val.m ? val : makeNull(); | ||
| 280 | } else { | 283 | } else { |
| 281 | throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary"); | 284 | throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary"); |
| 282 | } | 285 | } |
| @@ -285,15 +288,11 @@ JSON::addDictionaryMember(std::string const& key, JSON const& val) | @@ -285,15 +288,11 @@ JSON::addDictionaryMember(std::string const& key, JSON const& val) | ||
| 285 | bool | 288 | bool |
| 286 | JSON::checkDictionaryKeySeen(std::string const& key) | 289 | JSON::checkDictionaryKeySeen(std::string const& key) |
| 287 | { | 290 | { |
| 288 | - auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get()); | ||
| 289 | - if (nullptr == obj) { | ||
| 290 | - throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary"); | ||
| 291 | - } | ||
| 292 | - if (obj->parsed_keys.count(key)) { | ||
| 293 | - return true; | 291 | + if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) { |
| 292 | + return !obj->parsed_keys.insert(key).second; | ||
| 294 | } | 293 | } |
| 295 | - obj->parsed_keys.insert(key); | ||
| 296 | - return false; | 294 | + throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary"); |
| 295 | + return false; // unreachable | ||
| 297 | } | 296 | } |
| 298 | 297 | ||
| 299 | JSON | 298 | JSON |
| @@ -305,16 +304,16 @@ JSON::makeArray() | @@ -305,16 +304,16 @@ JSON::makeArray() | ||
| 305 | JSON | 304 | JSON |
| 306 | JSON::addArrayElement(JSON const& val) | 305 | JSON::addArrayElement(JSON const& val) |
| 307 | { | 306 | { |
| 308 | - auto* arr = dynamic_cast<JSON_array*>(m->value.get()); | ||
| 309 | - if (nullptr == arr) { | ||
| 310 | - throw std::runtime_error("JSON::addArrayElement called on non-array"); | ||
| 311 | - } | ||
| 312 | - if (val.m->value.get()) { | ||
| 313 | - arr->elements.push_back(val); | ||
| 314 | - } else { | ||
| 315 | - arr->elements.push_back(makeNull()); | 307 | + if (auto* arr = m ? dynamic_cast<JSON_array*>(m->value.get()) : nullptr) { |
| 308 | + if (val.m) { | ||
| 309 | + arr->elements.push_back(val); | ||
| 310 | + } else { | ||
| 311 | + arr->elements.push_back(makeNull()); | ||
| 312 | + } | ||
| 313 | + return arr->elements.back(); | ||
| 316 | } | 314 | } |
| 317 | - return arr->elements.back(); | 315 | + throw std::runtime_error("JSON::addArrayElement called on non-array"); |
| 316 | + return {}; // unreachable | ||
| 318 | } | 317 | } |
| 319 | 318 | ||
| 320 | JSON | 319 | JSON |
| @@ -362,19 +361,19 @@ JSON::makeBlob(std::function<void(Pipeline*)> fn) | @@ -362,19 +361,19 @@ JSON::makeBlob(std::function<void(Pipeline*)> fn) | ||
| 362 | bool | 361 | bool |
| 363 | JSON::isArray() const | 362 | JSON::isArray() const |
| 364 | { | 363 | { |
| 365 | - return m->value->type_code == vt_array; | 364 | + return m ? m->value->type_code == vt_array : false; |
| 366 | } | 365 | } |
| 367 | 366 | ||
| 368 | bool | 367 | bool |
| 369 | JSON::isDictionary() const | 368 | JSON::isDictionary() const |
| 370 | { | 369 | { |
| 371 | - return m->value->type_code == vt_dictionary; | 370 | + return m && m->value->type_code == vt_dictionary; |
| 372 | } | 371 | } |
| 373 | 372 | ||
| 374 | bool | 373 | bool |
| 375 | JSON::getString(std::string& utf8) const | 374 | JSON::getString(std::string& utf8) const |
| 376 | { | 375 | { |
| 377 | - if (m->value->type_code == vt_string) { | 376 | + if (m && m->value->type_code == vt_string) { |
| 378 | auto v = dynamic_cast<JSON_string const*>(m->value.get()); | 377 | auto v = dynamic_cast<JSON_string const*>(m->value.get()); |
| 379 | utf8 = v->utf8; | 378 | utf8 = v->utf8; |
| 380 | return true; | 379 | return true; |
| @@ -385,7 +384,7 @@ JSON::getString(std::string& utf8) const | @@ -385,7 +384,7 @@ JSON::getString(std::string& utf8) const | ||
| 385 | bool | 384 | bool |
| 386 | JSON::getNumber(std::string& value) const | 385 | JSON::getNumber(std::string& value) const |
| 387 | { | 386 | { |
| 388 | - if (m->value->type_code == vt_number) { | 387 | + if (m && m->value->type_code == vt_number) { |
| 389 | auto v = dynamic_cast<JSON_number const*>(m->value.get()); | 388 | auto v = dynamic_cast<JSON_number const*>(m->value.get()); |
| 390 | value = v->encoded; | 389 | value = v->encoded; |
| 391 | return true; | 390 | return true; |
| @@ -396,7 +395,7 @@ JSON::getNumber(std::string& value) const | @@ -396,7 +395,7 @@ JSON::getNumber(std::string& value) const | ||
| 396 | bool | 395 | bool |
| 397 | JSON::getBool(bool& value) const | 396 | JSON::getBool(bool& value) const |
| 398 | { | 397 | { |
| 399 | - if (m->value->type_code == vt_bool) { | 398 | + if (m && m->value->type_code == vt_bool) { |
| 400 | auto v = dynamic_cast<JSON_bool const*>(m->value.get()); | 399 | auto v = dynamic_cast<JSON_bool const*>(m->value.get()); |
| 401 | value = v->value; | 400 | value = v->value; |
| 402 | return true; | 401 | return true; |
| @@ -407,13 +406,13 @@ JSON::getBool(bool& value) const | @@ -407,13 +406,13 @@ JSON::getBool(bool& value) const | ||
| 407 | bool | 406 | bool |
| 408 | JSON::isNull() const | 407 | JSON::isNull() const |
| 409 | { | 408 | { |
| 410 | - return m->value->type_code == vt_null; | 409 | + return m && m->value->type_code == vt_null; |
| 411 | } | 410 | } |
| 412 | 411 | ||
| 413 | JSON | 412 | JSON |
| 414 | JSON::getDictItem(std::string const& key) const | 413 | JSON::getDictItem(std::string const& key) const |
| 415 | { | 414 | { |
| 416 | - if (auto v = dynamic_cast<JSON_dictionary const*>(m->value.get())) { | 415 | + if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) { |
| 417 | if (auto it = v->members.find(key); it != v->members.end()) { | 416 | if (auto it = v->members.find(key); it != v->members.end()) { |
| 418 | return it->second; | 417 | return it->second; |
| 419 | } | 418 | } |
| @@ -424,39 +423,37 @@ JSON::getDictItem(std::string const& key) const | @@ -424,39 +423,37 @@ JSON::getDictItem(std::string const& key) const | ||
| 424 | bool | 423 | bool |
| 425 | JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const | 424 | JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const |
| 426 | { | 425 | { |
| 427 | - auto v = dynamic_cast<JSON_dictionary const*>(m->value.get()); | ||
| 428 | - if (v == nullptr) { | ||
| 429 | - return false; | ||
| 430 | - } | ||
| 431 | - for (auto const& k: v->members) { | ||
| 432 | - fn(k.first, JSON(k.second)); | 426 | + if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) { |
| 427 | + for (auto const& [key, value]: v->members) { | ||
| 428 | + fn(key, value); | ||
| 429 | + } | ||
| 430 | + return true; | ||
| 433 | } | 431 | } |
| 434 | - return true; | 432 | + return false; |
| 435 | } | 433 | } |
| 436 | 434 | ||
| 437 | bool | 435 | bool |
| 438 | JSON::forEachArrayItem(std::function<void(JSON value)> fn) const | 436 | JSON::forEachArrayItem(std::function<void(JSON value)> fn) const |
| 439 | { | 437 | { |
| 440 | - auto v = dynamic_cast<JSON_array const*>(m->value.get()); | ||
| 441 | - if (v == nullptr) { | ||
| 442 | - return false; | ||
| 443 | - } | ||
| 444 | - for (auto const& i: v->elements) { | ||
| 445 | - fn(JSON(i)); | 438 | + if (auto v = m ? dynamic_cast<JSON_array const*>(m->value.get()) : nullptr) { |
| 439 | + for (auto const& i: v->elements) { | ||
| 440 | + fn(JSON(i)); | ||
| 441 | + } | ||
| 442 | + return true; | ||
| 446 | } | 443 | } |
| 447 | - return true; | 444 | + return false; |
| 448 | } | 445 | } |
| 449 | 446 | ||
| 450 | bool | 447 | bool |
| 451 | JSON::checkSchema(JSON schema, std::list<std::string>& errors) | 448 | JSON::checkSchema(JSON schema, std::list<std::string>& errors) |
| 452 | { | 449 | { |
| 453 | - return checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, ""); | 450 | + return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, ""); |
| 454 | } | 451 | } |
| 455 | 452 | ||
| 456 | bool | 453 | bool |
| 457 | JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors) | 454 | JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors) |
| 458 | { | 455 | { |
| 459 | - return checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, ""); | 456 | + return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, ""); |
| 460 | } | 457 | } |
| 461 | 458 | ||
| 462 | bool | 459 | bool |
| @@ -1374,23 +1371,27 @@ JSON::parse(std::string const& s) | @@ -1374,23 +1371,27 @@ JSON::parse(std::string const& s) | ||
| 1374 | void | 1371 | void |
| 1375 | JSON::setStart(qpdf_offset_t start) | 1372 | JSON::setStart(qpdf_offset_t start) |
| 1376 | { | 1373 | { |
| 1377 | - m->start = start; | 1374 | + if (m) { |
| 1375 | + m->start = start; | ||
| 1376 | + } | ||
| 1378 | } | 1377 | } |
| 1379 | 1378 | ||
| 1380 | void | 1379 | void |
| 1381 | JSON::setEnd(qpdf_offset_t end) | 1380 | JSON::setEnd(qpdf_offset_t end) |
| 1382 | { | 1381 | { |
| 1383 | - m->end = end; | 1382 | + if (m) { |
| 1383 | + m->end = end; | ||
| 1384 | + } | ||
| 1384 | } | 1385 | } |
| 1385 | 1386 | ||
| 1386 | qpdf_offset_t | 1387 | qpdf_offset_t |
| 1387 | JSON::getStart() const | 1388 | JSON::getStart() const |
| 1388 | { | 1389 | { |
| 1389 | - return m->start; | 1390 | + return m ? m->start : 0; |
| 1390 | } | 1391 | } |
| 1391 | 1392 | ||
| 1392 | qpdf_offset_t | 1393 | qpdf_offset_t |
| 1393 | JSON::getEnd() const | 1394 | JSON::getEnd() const |
| 1394 | { | 1395 | { |
| 1395 | - return m->end; | 1396 | + return m ? m->end : 0; |
| 1396 | } | 1397 | } |
libtests/json.cc
| @@ -2,8 +2,10 @@ | @@ -2,8 +2,10 @@ | ||
| 2 | 2 | ||
| 3 | #include <qpdf/JSON.hh> | 3 | #include <qpdf/JSON.hh> |
| 4 | #include <qpdf/Pipeline.hh> | 4 | #include <qpdf/Pipeline.hh> |
| 5 | +#include <qpdf/Pl_String.hh> | ||
| 5 | #include <qpdf/QPDF.hh> | 6 | #include <qpdf/QPDF.hh> |
| 6 | #include <qpdf/QPDFObjectHandle.hh> | 7 | #include <qpdf/QPDFObjectHandle.hh> |
| 8 | + | ||
| 7 | #include <iostream> | 9 | #include <iostream> |
| 8 | 10 | ||
| 9 | static void | 11 | static void |
| @@ -131,6 +133,51 @@ test_main() | @@ -131,6 +133,51 @@ test_main() | ||
| 131 | " \"blob\": \"AQIDBAX//v38+w==\",\n" | 133 | " \"blob\": \"AQIDBAX//v38+w==\",\n" |
| 132 | " \"normal\": \"string\"\n" | 134 | " \"normal\": \"string\"\n" |
| 133 | "}"); | 135 | "}"); |
| 136 | + | ||
| 137 | + // Check default constructed JSON object (order as per JSON.hh). | ||
| 138 | + JSON uninitialized; | ||
| 139 | + std::string ws; | ||
| 140 | + auto pl = Pl_String ("", nullptr, ws); | ||
| 141 | + uninitialized.write(&pl); | ||
| 142 | + assert(ws == "null"); | ||
| 143 | + assert(uninitialized.unparse() == "null"); | ||
| 144 | + try { | ||
| 145 | + uninitialized.addDictionaryMember("key", jarr); | ||
| 146 | + assert(false); | ||
| 147 | + } catch (std::runtime_error&) { | ||
| 148 | + } | ||
| 149 | + assert(jmap.addDictionaryMember("42", uninitialized).isNull()); | ||
| 150 | + try { | ||
| 151 | + uninitialized.addArrayElement(jarr); | ||
| 152 | + assert(false); | ||
| 153 | + } catch (std::runtime_error&) { | ||
| 154 | + } | ||
| 155 | + assert(jarr.addArrayElement(uninitialized).isNull()); | ||
| 156 | + assert(!uninitialized.isArray()); | ||
| 157 | + assert(!uninitialized.isDictionary()); | ||
| 158 | + try { | ||
| 159 | + uninitialized.checkDictionaryKeySeen("key"); | ||
| 160 | + assert(false); | ||
| 161 | + } catch (std::logic_error&) { | ||
| 162 | + } | ||
| 163 | + std::string st_out = "unchanged"; | ||
| 164 | + assert(!uninitialized.getString(st_out)); | ||
| 165 | + assert(!uninitialized.getNumber(st_out)); | ||
| 166 | + bool b_out = true; | ||
| 167 | + assert(!uninitialized.getBool(b_out)); | ||
| 168 | + assert(b_out && st_out == "unchanged"); | ||
| 169 | + assert(!uninitialized.isNull()); | ||
| 170 | + assert(uninitialized.getDictItem("42").isNull()); | ||
| 171 | + assert(!uninitialized.forEachDictItem([](auto k, auto v) {})); | ||
| 172 | + assert(!uninitialized.forEachArrayItem([](auto v) {})); | ||
| 173 | + std::list<std::string> e; | ||
| 174 | + assert(!uninitialized.checkSchema(JSON(), 0, e)); | ||
| 175 | + assert(!uninitialized.checkSchema(JSON(), e)); | ||
| 176 | + assert(e.empty()); | ||
| 177 | + uninitialized.setStart(0); | ||
| 178 | + uninitialized.setEnd(0); | ||
| 179 | + assert(uninitialized.getStart() == 0); | ||
| 180 | + assert(uninitialized.getEnd() == 0); | ||
| 134 | } | 181 | } |
| 135 | 182 | ||
| 136 | static void | 183 | static void |