mirror of
https://github.com/qpdf/qpdf.git
synced 2024-11-01 03:12:29 +00:00
Handle default-constructed JSON objects
This commit is contained in:
parent
6b80e0f14b
commit
b6a2b5d3c1
@ -191,7 +191,7 @@ JSON::JSON_blob::write(Pipeline* p, size_t) const
|
|||||||
void
|
void
|
||||||
JSON::write(Pipeline* p, size_t depth) const
|
JSON::write(Pipeline* p, size_t depth) const
|
||||||
{
|
{
|
||||||
if (nullptr == m->value) {
|
if (!m) {
|
||||||
*p << "null";
|
*p << "null";
|
||||||
} else {
|
} else {
|
||||||
m->value->write(p, depth);
|
m->value->write(p, depth);
|
||||||
@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const
|
|||||||
std::string
|
std::string
|
||||||
JSON::unparse() const
|
JSON::unparse() const
|
||||||
{
|
{
|
||||||
|
if (!m) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
std::string s;
|
std::string s;
|
||||||
Pl_String p("unparse", nullptr, s);
|
Pl_String p("unparse", nullptr, s);
|
||||||
write(&p, 0);
|
write(&p, 0);
|
||||||
@ -275,8 +278,8 @@ JSON::makeDictionary()
|
|||||||
JSON
|
JSON
|
||||||
JSON::addDictionaryMember(std::string const& key, JSON const& val)
|
JSON::addDictionaryMember(std::string const& key, JSON const& val)
|
||||||
{
|
{
|
||||||
if (auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get())) {
|
if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) {
|
||||||
return obj->members[encode_string(key)] = val.m->value ? val : makeNull();
|
return obj->members[encode_string(key)] = val.m ? val : makeNull();
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary");
|
throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary");
|
||||||
}
|
}
|
||||||
@ -285,15 +288,11 @@ JSON::addDictionaryMember(std::string const& key, JSON const& val)
|
|||||||
bool
|
bool
|
||||||
JSON::checkDictionaryKeySeen(std::string const& key)
|
JSON::checkDictionaryKeySeen(std::string const& key)
|
||||||
{
|
{
|
||||||
auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get());
|
if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) {
|
||||||
if (nullptr == obj) {
|
return !obj->parsed_keys.insert(key).second;
|
||||||
|
}
|
||||||
throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary");
|
throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary");
|
||||||
}
|
return false; // unreachable
|
||||||
if (obj->parsed_keys.count(key)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
obj->parsed_keys.insert(key);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON
|
JSON
|
||||||
@ -305,17 +304,17 @@ JSON::makeArray()
|
|||||||
JSON
|
JSON
|
||||||
JSON::addArrayElement(JSON const& val)
|
JSON::addArrayElement(JSON const& val)
|
||||||
{
|
{
|
||||||
auto* arr = dynamic_cast<JSON_array*>(m->value.get());
|
if (auto* arr = m ? dynamic_cast<JSON_array*>(m->value.get()) : nullptr) {
|
||||||
if (nullptr == arr) {
|
if (val.m) {
|
||||||
throw std::runtime_error("JSON::addArrayElement called on non-array");
|
|
||||||
}
|
|
||||||
if (val.m->value.get()) {
|
|
||||||
arr->elements.push_back(val);
|
arr->elements.push_back(val);
|
||||||
} else {
|
} else {
|
||||||
arr->elements.push_back(makeNull());
|
arr->elements.push_back(makeNull());
|
||||||
}
|
}
|
||||||
return arr->elements.back();
|
return arr->elements.back();
|
||||||
}
|
}
|
||||||
|
throw std::runtime_error("JSON::addArrayElement called on non-array");
|
||||||
|
return {}; // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
JSON
|
JSON
|
||||||
JSON::makeString(std::string const& utf8)
|
JSON::makeString(std::string const& utf8)
|
||||||
@ -362,19 +361,19 @@ JSON::makeBlob(std::function<void(Pipeline*)> fn)
|
|||||||
bool
|
bool
|
||||||
JSON::isArray() const
|
JSON::isArray() const
|
||||||
{
|
{
|
||||||
return m->value->type_code == vt_array;
|
return m ? m->value->type_code == vt_array : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSON::isDictionary() const
|
JSON::isDictionary() const
|
||||||
{
|
{
|
||||||
return m->value->type_code == vt_dictionary;
|
return m && m->value->type_code == vt_dictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSON::getString(std::string& utf8) const
|
JSON::getString(std::string& utf8) const
|
||||||
{
|
{
|
||||||
if (m->value->type_code == vt_string) {
|
if (m && m->value->type_code == vt_string) {
|
||||||
auto v = dynamic_cast<JSON_string const*>(m->value.get());
|
auto v = dynamic_cast<JSON_string const*>(m->value.get());
|
||||||
utf8 = v->utf8;
|
utf8 = v->utf8;
|
||||||
return true;
|
return true;
|
||||||
@ -385,7 +384,7 @@ JSON::getString(std::string& utf8) const
|
|||||||
bool
|
bool
|
||||||
JSON::getNumber(std::string& value) const
|
JSON::getNumber(std::string& value) const
|
||||||
{
|
{
|
||||||
if (m->value->type_code == vt_number) {
|
if (m && m->value->type_code == vt_number) {
|
||||||
auto v = dynamic_cast<JSON_number const*>(m->value.get());
|
auto v = dynamic_cast<JSON_number const*>(m->value.get());
|
||||||
value = v->encoded;
|
value = v->encoded;
|
||||||
return true;
|
return true;
|
||||||
@ -396,7 +395,7 @@ JSON::getNumber(std::string& value) const
|
|||||||
bool
|
bool
|
||||||
JSON::getBool(bool& value) const
|
JSON::getBool(bool& value) const
|
||||||
{
|
{
|
||||||
if (m->value->type_code == vt_bool) {
|
if (m && m->value->type_code == vt_bool) {
|
||||||
auto v = dynamic_cast<JSON_bool const*>(m->value.get());
|
auto v = dynamic_cast<JSON_bool const*>(m->value.get());
|
||||||
value = v->value;
|
value = v->value;
|
||||||
return true;
|
return true;
|
||||||
@ -407,13 +406,13 @@ JSON::getBool(bool& value) const
|
|||||||
bool
|
bool
|
||||||
JSON::isNull() const
|
JSON::isNull() const
|
||||||
{
|
{
|
||||||
return m->value->type_code == vt_null;
|
return m && m->value->type_code == vt_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON
|
JSON
|
||||||
JSON::getDictItem(std::string const& key) const
|
JSON::getDictItem(std::string const& key) const
|
||||||
{
|
{
|
||||||
if (auto v = dynamic_cast<JSON_dictionary const*>(m->value.get())) {
|
if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) {
|
||||||
if (auto it = v->members.find(key); it != v->members.end()) {
|
if (auto it = v->members.find(key); it != v->members.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
@ -424,39 +423,37 @@ JSON::getDictItem(std::string const& key) const
|
|||||||
bool
|
bool
|
||||||
JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const
|
JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const
|
||||||
{
|
{
|
||||||
auto v = dynamic_cast<JSON_dictionary const*>(m->value.get());
|
if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) {
|
||||||
if (v == nullptr) {
|
for (auto const& [key, value]: v->members) {
|
||||||
return false;
|
fn(key, value);
|
||||||
}
|
|
||||||
for (auto const& k: v->members) {
|
|
||||||
fn(k.first, JSON(k.second));
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSON::forEachArrayItem(std::function<void(JSON value)> fn) const
|
JSON::forEachArrayItem(std::function<void(JSON value)> fn) const
|
||||||
{
|
{
|
||||||
auto v = dynamic_cast<JSON_array const*>(m->value.get());
|
if (auto v = m ? dynamic_cast<JSON_array const*>(m->value.get()) : nullptr) {
|
||||||
if (v == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (auto const& i: v->elements) {
|
for (auto const& i: v->elements) {
|
||||||
fn(JSON(i));
|
fn(JSON(i));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSON::checkSchema(JSON schema, std::list<std::string>& errors)
|
JSON::checkSchema(JSON schema, std::list<std::string>& errors)
|
||||||
{
|
{
|
||||||
return checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, "");
|
return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors)
|
JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors)
|
||||||
{
|
{
|
||||||
return checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, "");
|
return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -1374,23 +1371,27 @@ JSON::parse(std::string const& s)
|
|||||||
void
|
void
|
||||||
JSON::setStart(qpdf_offset_t start)
|
JSON::setStart(qpdf_offset_t start)
|
||||||
{
|
{
|
||||||
|
if (m) {
|
||||||
m->start = start;
|
m->start = start;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSON::setEnd(qpdf_offset_t end)
|
JSON::setEnd(qpdf_offset_t end)
|
||||||
{
|
{
|
||||||
|
if (m) {
|
||||||
m->end = end;
|
m->end = end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qpdf_offset_t
|
qpdf_offset_t
|
||||||
JSON::getStart() const
|
JSON::getStart() const
|
||||||
{
|
{
|
||||||
return m->start;
|
return m ? m->start : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
qpdf_offset_t
|
qpdf_offset_t
|
||||||
JSON::getEnd() const
|
JSON::getEnd() const
|
||||||
{
|
{
|
||||||
return m->end;
|
return m ? m->end : 0;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#include <qpdf/JSON.hh>
|
#include <qpdf/JSON.hh>
|
||||||
#include <qpdf/Pipeline.hh>
|
#include <qpdf/Pipeline.hh>
|
||||||
|
#include <qpdf/Pl_String.hh>
|
||||||
#include <qpdf/QPDF.hh>
|
#include <qpdf/QPDF.hh>
|
||||||
#include <qpdf/QPDFObjectHandle.hh>
|
#include <qpdf/QPDFObjectHandle.hh>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -131,6 +133,51 @@ test_main()
|
|||||||
" \"blob\": \"AQIDBAX//v38+w==\",\n"
|
" \"blob\": \"AQIDBAX//v38+w==\",\n"
|
||||||
" \"normal\": \"string\"\n"
|
" \"normal\": \"string\"\n"
|
||||||
"}");
|
"}");
|
||||||
|
|
||||||
|
// Check default constructed JSON object (order as per JSON.hh).
|
||||||
|
JSON uninitialized;
|
||||||
|
std::string ws;
|
||||||
|
auto pl = Pl_String ("", nullptr, ws);
|
||||||
|
uninitialized.write(&pl);
|
||||||
|
assert(ws == "null");
|
||||||
|
assert(uninitialized.unparse() == "null");
|
||||||
|
try {
|
||||||
|
uninitialized.addDictionaryMember("key", jarr);
|
||||||
|
assert(false);
|
||||||
|
} catch (std::runtime_error&) {
|
||||||
|
}
|
||||||
|
assert(jmap.addDictionaryMember("42", uninitialized).isNull());
|
||||||
|
try {
|
||||||
|
uninitialized.addArrayElement(jarr);
|
||||||
|
assert(false);
|
||||||
|
} catch (std::runtime_error&) {
|
||||||
|
}
|
||||||
|
assert(jarr.addArrayElement(uninitialized).isNull());
|
||||||
|
assert(!uninitialized.isArray());
|
||||||
|
assert(!uninitialized.isDictionary());
|
||||||
|
try {
|
||||||
|
uninitialized.checkDictionaryKeySeen("key");
|
||||||
|
assert(false);
|
||||||
|
} catch (std::logic_error&) {
|
||||||
|
}
|
||||||
|
std::string st_out = "unchanged";
|
||||||
|
assert(!uninitialized.getString(st_out));
|
||||||
|
assert(!uninitialized.getNumber(st_out));
|
||||||
|
bool b_out = true;
|
||||||
|
assert(!uninitialized.getBool(b_out));
|
||||||
|
assert(b_out && st_out == "unchanged");
|
||||||
|
assert(!uninitialized.isNull());
|
||||||
|
assert(uninitialized.getDictItem("42").isNull());
|
||||||
|
assert(!uninitialized.forEachDictItem([](auto k, auto v) {}));
|
||||||
|
assert(!uninitialized.forEachArrayItem([](auto v) {}));
|
||||||
|
std::list<std::string> e;
|
||||||
|
assert(!uninitialized.checkSchema(JSON(), 0, e));
|
||||||
|
assert(!uninitialized.checkSchema(JSON(), e));
|
||||||
|
assert(e.empty());
|
||||||
|
uninitialized.setStart(0);
|
||||||
|
uninitialized.setEnd(0);
|
||||||
|
assert(uninitialized.getStart() == 0);
|
||||||
|
assert(uninitialized.getEnd() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Loading…
Reference in New Issue
Block a user