2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-09-27 20:49:06 +00:00
qpdf/libqpdf/JSONHandler.cc
Jay Berkenbilt ce3406e93f JSONHandler: pass JSON object to dict start function
If some keys depend on others, we have to check up front since there
is no control of what order key handlers will be called. Anyway, keys
are unordered in json, so we don't want to depend on ordering.
2022-01-31 15:57:45 -05:00

170 lines
4.2 KiB
C++

#include <qpdf/JSONHandler.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QPDFUsage.hh>
JSONHandler::JSONHandler() :
m(new Members())
{
}
JSONHandler::Members::Members()
{
}
void
JSONHandler::usage(std::string const& msg)
{
throw QPDFUsage(msg);
}
void
JSONHandler::addAnyHandler(json_handler_t fn)
{
this->m->h.any_handler = fn;
}
void
JSONHandler::addNullHandler(void_handler_t fn)
{
this->m->h.null_handler = fn;
}
void
JSONHandler::addStringHandler(string_handler_t fn)
{
this->m->h.string_handler = fn;
}
void
JSONHandler::addNumberHandler(string_handler_t fn)
{
this->m->h.number_handler = fn;
}
void
JSONHandler::addBoolHandler(bool_handler_t fn)
{
this->m->h.bool_handler = fn;
}
void
JSONHandler::addDictHandlers(json_handler_t start_fn, void_handler_t end_fn)
{
this->m->h.dict_start_handler = start_fn;
this->m->h.dict_end_handler = end_fn;
}
void
JSONHandler::addDictKeyHandler(
std::string const& key, std::shared_ptr<JSONHandler> dkh)
{
this->m->h.dict_handlers[key] = dkh;
}
void
JSONHandler::addFallbackDictHandler(std::shared_ptr<JSONHandler> fdh)
{
this->m->h.fallback_dict_handler = fdh;
}
void
JSONHandler::addArrayHandlers(void_handler_t start_fn,
void_handler_t end_fn,
std::shared_ptr<JSONHandler> ah)
{
this->m->h.array_start_handler = start_fn;
this->m->h.array_end_handler = end_fn;
this->m->h.array_item_handler = ah;
}
void
JSONHandler::handle(std::string const& path, JSON j)
{
if (this->m->h.any_handler)
{
this->m->h.any_handler(path, j);
return;
}
bool handled = false;
bool bvalue = false;
std::string s_value;
if (this->m->h.null_handler && j.isNull())
{
this->m->h.null_handler(path);
handled = true;
}
if (this->m->h.string_handler && j.getString(s_value))
{
this->m->h.string_handler(path, s_value);
handled = true;
}
if (this->m->h.number_handler && j.getNumber(s_value))
{
this->m->h.number_handler(path, s_value);
handled = true;
}
if (this->m->h.bool_handler && j.getBool(bvalue))
{
this->m->h.bool_handler(path, bvalue);
handled = true;
}
if (this->m->h.dict_start_handler && j.isDictionary())
{
this->m->h.dict_start_handler(path, j);
std::string path_base = path;
if (path_base != ".")
{
path_base += ".";
}
j.forEachDictItem([&path, &path_base, this](
std::string const& k, JSON v) {
auto i = this->m->h.dict_handlers.find(k);
if (i == this->m->h.dict_handlers.end())
{
if (this->m->h.fallback_dict_handler.get())
{
this->m->h.fallback_dict_handler->handle(
path_base + k, v);
}
else
{
QTC::TC("libtests", "JSONHandler unexpected key");
usage("JSON handler found unexpected key " + k +
" in object at " + path);
}
}
else
{
i->second->handle(path_base + k, v);
}
});
this->m->h.dict_end_handler(path);
handled = true;
}
if (this->m->h.array_start_handler && j.isArray())
{
this->m->h.array_start_handler(path);
size_t i = 0;
j.forEachArrayItem([&i, &path, this](JSON v) {
this->m->h.array_item_handler->handle(
path + "[" + QUtil::uint_to_string(i) + "]", v);
++i;
});
this->m->h.array_end_handler(path);
handled = true;
}
if (! handled)
{
// It would be nice to include information about what type the
// object was and what types were allowed, but we're relying
// on schema validation to make sure input is properly
// structured before calling the handlers. It would be
// different if this code were trying to be part of a
// general-purpose JSON package.
QTC::TC("libtests", "JSONHandler unhandled value");
usage("JSON handler: value at " + path + " is not of expected type");
}
}