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 | 191 | void |
| 192 | 192 | JSON::write(Pipeline* p, size_t depth) const |
| 193 | 193 | { |
| 194 | - if (nullptr == m->value) { | |
| 194 | + if (!m) { | |
| 195 | 195 | *p << "null"; |
| 196 | 196 | } else { |
| 197 | 197 | m->value->write(p, depth); |
| ... | ... | @@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const |
| 201 | 201 | std::string |
| 202 | 202 | JSON::unparse() const |
| 203 | 203 | { |
| 204 | + if (!m) { | |
| 205 | + return "null"; | |
| 206 | + } | |
| 204 | 207 | std::string s; |
| 205 | 208 | Pl_String p("unparse", nullptr, s); |
| 206 | 209 | write(&p, 0); |
| ... | ... | @@ -275,8 +278,8 @@ JSON::makeDictionary() |
| 275 | 278 | JSON |
| 276 | 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 | 283 | } else { |
| 281 | 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 | 288 | bool |
| 286 | 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 | 298 | JSON |
| ... | ... | @@ -305,16 +304,16 @@ JSON::makeArray() |
| 305 | 304 | JSON |
| 306 | 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 | 319 | JSON |
| ... | ... | @@ -362,19 +361,19 @@ JSON::makeBlob(std::function<void(Pipeline*)> fn) |
| 362 | 361 | bool |
| 363 | 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 | 367 | bool |
| 369 | 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 | 373 | bool |
| 375 | 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 | 377 | auto v = dynamic_cast<JSON_string const*>(m->value.get()); |
| 379 | 378 | utf8 = v->utf8; |
| 380 | 379 | return true; |
| ... | ... | @@ -385,7 +384,7 @@ JSON::getString(std::string& utf8) const |
| 385 | 384 | bool |
| 386 | 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 | 388 | auto v = dynamic_cast<JSON_number const*>(m->value.get()); |
| 390 | 389 | value = v->encoded; |
| 391 | 390 | return true; |
| ... | ... | @@ -396,7 +395,7 @@ JSON::getNumber(std::string& value) const |
| 396 | 395 | bool |
| 397 | 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 | 399 | auto v = dynamic_cast<JSON_bool const*>(m->value.get()); |
| 401 | 400 | value = v->value; |
| 402 | 401 | return true; |
| ... | ... | @@ -407,13 +406,13 @@ JSON::getBool(bool& value) const |
| 407 | 406 | bool |
| 408 | 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 | 412 | JSON |
| 414 | 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 | 416 | if (auto it = v->members.find(key); it != v->members.end()) { |
| 418 | 417 | return it->second; |
| 419 | 418 | } |
| ... | ... | @@ -424,39 +423,37 @@ JSON::getDictItem(std::string const& key) const |
| 424 | 423 | bool |
| 425 | 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 | 435 | bool |
| 438 | 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 | 447 | bool |
| 451 | 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 | 453 | bool |
| 457 | 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 | 459 | bool |
| ... | ... | @@ -1374,23 +1371,27 @@ JSON::parse(std::string const& s) |
| 1374 | 1371 | void |
| 1375 | 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 | 1379 | void |
| 1381 | 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 | 1387 | qpdf_offset_t |
| 1387 | 1388 | JSON::getStart() const |
| 1388 | 1389 | { |
| 1389 | - return m->start; | |
| 1390 | + return m ? m->start : 0; | |
| 1390 | 1391 | } |
| 1391 | 1392 | |
| 1392 | 1393 | qpdf_offset_t |
| 1393 | 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 | 2 | |
| 3 | 3 | #include <qpdf/JSON.hh> |
| 4 | 4 | #include <qpdf/Pipeline.hh> |
| 5 | +#include <qpdf/Pl_String.hh> | |
| 5 | 6 | #include <qpdf/QPDF.hh> |
| 6 | 7 | #include <qpdf/QPDFObjectHandle.hh> |
| 8 | + | |
| 7 | 9 | #include <iostream> |
| 8 | 10 | |
| 9 | 11 | static void |
| ... | ... | @@ -131,6 +133,51 @@ test_main() |
| 131 | 133 | " \"blob\": \"AQIDBAX//v38+w==\",\n" |
| 132 | 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 | 183 | static void | ... | ... |