2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-09-28 21:19:06 +00:00

JSONHandler: rework dictionary and array handlers

This commit is contained in:
Jay Berkenbilt 2022-01-20 08:53:53 -05:00
parent acf8d18b6e
commit 1db0a7ffce
4 changed files with 89 additions and 40 deletions

View File

@ -31,6 +31,11 @@
#include <stdexcept> #include <stdexcept>
#include <memory> #include <memory>
// This class allows a sax-like walk through a JSON object with
// functionality that mostly mirrors QPDFArgParser. It is primarily
// here to facilitate automatic generation of some of the code to help
// keep QPDFJob json consistent with command-line arguments.
class JSONHandler class JSONHandler
{ {
public: public:
@ -53,7 +58,8 @@ class JSONHandler
// certain type. JSONHandler::Error is thrown otherwise. Multiple // certain type. JSONHandler::Error is thrown otherwise. Multiple
// handlers may be registered, which allows the object to be of // handlers may be registered, which allows the object to be of
// various types. If an anyHandler is added, no other handler will // various types. If an anyHandler is added, no other handler will
// be called. // be called. There is no "final" handler -- if the top-level is a
// dictionary or array, just use its end handler.
typedef std::function<void( typedef std::function<void(
std::string const& path, JSON value)> json_handler_t; std::string const& path, JSON value)> json_handler_t;
@ -80,19 +86,18 @@ class JSONHandler
QPDF_DLL QPDF_DLL
void addBoolHandler(bool_handler_t fn); void addBoolHandler(bool_handler_t fn);
// Returns a reference to a map: keys are expected object keys,
// and values are handlers for that object.
QPDF_DLL QPDF_DLL
std::map<std::string, std::shared_ptr<JSONHandler>>& addDictHandlers(); void addDictHandlers(void_handler_t start_fn, void_handler_t end_fn);
QPDF_DLL
// Apply the given handler to any key not explicitly in dict void addDictKeyHandler(
// handlers. std::string const& key, std::shared_ptr<JSONHandler>);
QPDF_DLL QPDF_DLL
void addFallbackDictHandler(std::shared_ptr<JSONHandler>); void addFallbackDictHandler(std::shared_ptr<JSONHandler>);
// Apply the given handler to each element of the array.
QPDF_DLL QPDF_DLL
void addArrayHandler(std::shared_ptr<JSONHandler>); void addArrayHandlers(void_handler_t start_fn,
void_handler_t end_fn,
std::shared_ptr<JSONHandler> item_handlers);
// Apply handlers recursively to a JSON object. // Apply handlers recursively to a JSON object.
QPDF_DLL QPDF_DLL
@ -108,7 +113,12 @@ class JSONHandler
null_handler(nullptr), null_handler(nullptr),
string_handler(nullptr), string_handler(nullptr),
number_handler(nullptr), number_handler(nullptr),
bool_handler(nullptr) bool_handler(nullptr),
dict_start_handler(nullptr),
dict_end_handler(nullptr),
array_start_handler(nullptr),
array_end_handler(nullptr),
final_handler(nullptr)
{ {
} }
@ -117,9 +127,14 @@ class JSONHandler
string_handler_t string_handler; string_handler_t string_handler;
string_handler_t number_handler; string_handler_t number_handler;
bool_handler_t bool_handler; bool_handler_t bool_handler;
void_handler_t dict_start_handler;
void_handler_t dict_end_handler;
void_handler_t array_start_handler;
void_handler_t array_end_handler;
void_handler_t final_handler;
std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers; std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers;
std::shared_ptr<JSONHandler> fallback_dict_handler; std::shared_ptr<JSONHandler> fallback_dict_handler;
std::shared_ptr<JSONHandler> array_handler; std::shared_ptr<JSONHandler> array_item_handler;
}; };
class Members class Members

View File

@ -46,10 +46,18 @@ JSONHandler::addBoolHandler(bool_handler_t fn)
this->m->h.bool_handler = fn; this->m->h.bool_handler = fn;
} }
std::map<std::string, std::shared_ptr<JSONHandler>>& void
JSONHandler::addDictHandlers() JSONHandler::addDictHandlers(void_handler_t start_fn, void_handler_t end_fn)
{ {
return this->m->h.dict_handlers; 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 void
@ -59,9 +67,13 @@ JSONHandler::addFallbackDictHandler(std::shared_ptr<JSONHandler> fdh)
} }
void void
JSONHandler::addArrayHandler(std::shared_ptr<JSONHandler> ah) JSONHandler::addArrayHandlers(void_handler_t start_fn,
void_handler_t end_fn,
std::shared_ptr<JSONHandler> ah)
{ {
this->m->h.array_handler = 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 void
@ -95,9 +107,9 @@ JSONHandler::handle(std::string const& path, JSON j)
this->m->h.bool_handler(path, bvalue); this->m->h.bool_handler(path, bvalue);
handled = true; handled = true;
} }
if ((this->m->h.fallback_dict_handler.get() || if (this->m->h.dict_start_handler && j.isDictionary())
(! this->m->h.dict_handlers.empty())) && j.isDictionary())
{ {
this->m->h.dict_start_handler(path);
std::string path_base = path; std::string path_base = path;
if (path_base != ".") if (path_base != ".")
{ {
@ -126,22 +138,19 @@ JSONHandler::handle(std::string const& path, JSON j)
i->second->handle(path_base + k, v); i->second->handle(path_base + k, v);
} }
}); });
this->m->h.dict_end_handler(path);
// Set handled = true even if we didn't call any handlers.
// This dictionary could have been empty, but it's okay since
// it's a dictionary like it's supposed to be.
handled = true; handled = true;
} }
if (this->m->h.array_handler.get()) if (this->m->h.array_start_handler && j.isArray())
{ {
this->m->h.array_start_handler(path);
size_t i = 0; size_t i = 0;
j.forEachArrayItem([&i, &path, this](JSON v) { j.forEachArrayItem([&i, &path, this](JSON v) {
this->m->h.array_handler->handle( this->m->h.array_item_handler->handle(
path + "[" + QUtil::uint_to_string(i) + "]", v); path + "[" + QUtil::uint_to_string(i) + "]", v);
++i; ++i;
}); });
// Set handled = true even if we didn't call any handlers. this->m->h.array_end_handler(path);
// This could have been an empty array.
handled = true; handled = true;
} }

View File

@ -28,6 +28,13 @@ static void print_json(std::string const& path, JSON value)
std::cout << path << ": json: " << value.unparse() << std::endl; std::cout << path << ": json: " << value.unparse() << std::endl;
} }
static JSONHandler::void_handler_t make_print_message(std::string msg)
{
return [msg](std::string const& path) {
std::cout << path << ": json: " << msg << std::endl;
};
}
static void test_scalar() static void test_scalar()
{ {
std::cout << "-- scalar --" << std::endl; std::cout << "-- scalar --" << std::endl;
@ -40,41 +47,50 @@ static void test_scalar()
static std::shared_ptr<JSONHandler> make_all_handler() static std::shared_ptr<JSONHandler> make_all_handler()
{ {
auto h = std::make_shared<JSONHandler>(); auto h = std::make_shared<JSONHandler>();
auto& m = h->addDictHandlers(); h->addDictHandlers(
make_print_message("dict begin"),
make_print_message("dict end"));
auto h1 = std::make_shared<JSONHandler>(); auto h1 = std::make_shared<JSONHandler>();
h1->addStringHandler(print_string); h1->addStringHandler(print_string);
m["one"] = h1; h->addDictKeyHandler("one", h1);
auto h2 = std::make_shared<JSONHandler>(); auto h2 = std::make_shared<JSONHandler>();
h2->addNumberHandler(print_number); h2->addNumberHandler(print_number);
m["two"] = h2; h->addDictKeyHandler("two", h2);
auto h3 = std::make_shared<JSONHandler>(); auto h3 = std::make_shared<JSONHandler>();
h3->addBoolHandler(print_bool); h3->addBoolHandler(print_bool);
m["three"] = h3; h->addDictKeyHandler("three", h3);
auto h4 = std::make_shared<JSONHandler>(); auto h4 = std::make_shared<JSONHandler>();
h4->addAnyHandler(print_json); h4->addAnyHandler(print_json);
m["four"] = h4; h->addDictKeyHandler("four", h4);
m["phour"] = h4; // share h4 h->addDictKeyHandler("phour", h4); // share h4
auto h5 = std::make_shared<JSONHandler>(); auto h5 = std::make_shared<JSONHandler>();
// Allow to be either string or bool // Allow to be either string or bool
h5->addBoolHandler(print_bool); h5->addBoolHandler(print_bool);
h5->addStringHandler(print_string); h5->addStringHandler(print_string);
h5->addNullHandler(print_null); h5->addNullHandler(print_null);
auto h5s = std::make_shared<JSONHandler>(); auto h5s = std::make_shared<JSONHandler>();
m["five"] = h5s; h->addDictKeyHandler("five", h5s);
h5s->addArrayHandler(h5); h5s->addArrayHandlers(
make_print_message("array begin"),
make_print_message("array end"),
h5);
auto h6 = std::make_shared<JSONHandler>(); auto h6 = std::make_shared<JSONHandler>();
auto& m6 = h6->addDictHandlers(); h6->addDictHandlers(
make_print_message("dict begin"),
make_print_message("dict end"));
auto h6a = std::make_shared<JSONHandler>(); auto h6a = std::make_shared<JSONHandler>();
m6["a"] = h6a; h6->addDictKeyHandler("a", h6a);
auto& m6a = h6a->addDictHandlers(); h6a->addDictHandlers(
make_print_message("dict begin"),
make_print_message("dict end"));
auto h6ab = std::make_shared<JSONHandler>(); auto h6ab = std::make_shared<JSONHandler>();
m6a["b"] = h6ab; h6a->addDictKeyHandler("b", h6ab);
auto h6ax = std::make_shared<JSONHandler>(); auto h6ax = std::make_shared<JSONHandler>();
h6ax->addAnyHandler(print_json); h6ax->addAnyHandler(print_json);
h6a->addFallbackDictHandler(h6ax); h6a->addFallbackDictHandler(h6ax);
m6["b"] = h6ab; // share h6->addDictKeyHandler("b", h6ab); // share
h6ab->addStringHandler(print_string); h6ab->addStringHandler(print_string);
m["six"] = h6; h->addDictKeyHandler("six", h6);
return h; return h;
} }

View File

@ -1,22 +1,31 @@
-- scalar -- -- scalar --
.: string: potato .: string: potato
-- all -- -- all --
.: json: dict begin
.five: json: array begin
.five[0]: string: x .five[0]: string: x
.five[1]: bool: false .five[1]: bool: false
.five[2]: string: y .five[2]: string: y
.five[3]: null .five[3]: null
.five[4]: bool: true .five[4]: bool: true
.five: json: array end
.four: json: [ .four: json: [
"a", "a",
1 1
] ]
.one: string: potato .one: string: potato
.phour: json: null .phour: json: null
.six: json: dict begin
.six.a: json: dict begin
.six.a.Q: json: "baaa" .six.a.Q: json: "baaa"
.six.a.b: string: quack .six.a.b: string: quack
.six.a: json: dict end
.six.b: string: moo .six.b: string: moo
.six: json: dict end
.three: bool: true .three: bool: true
.two: number: 3.14 .two: number: 3.14
.: json: dict end
-- errors -- -- errors --
bad type at top: JSON handler: value at . is not of expected type bad type at top: JSON handler: value at . is not of expected type
.: json: dict begin
unexpected key: JSON handler found unexpected key x in object at . unexpected key: JSON handler found unexpected key x in object at .