2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-06-26 15:13:42 +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 <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
{
public:
@ -53,7 +58,8 @@ class JSONHandler
// certain type. JSONHandler::Error is thrown otherwise. Multiple
// handlers may be registered, which allows the object to be of
// 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(
std::string const& path, JSON value)> json_handler_t;
@ -80,19 +86,18 @@ class JSONHandler
QPDF_DLL
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
std::map<std::string, std::shared_ptr<JSONHandler>>& addDictHandlers();
// Apply the given handler to any key not explicitly in dict
// handlers.
void addDictHandlers(void_handler_t start_fn, void_handler_t end_fn);
QPDF_DLL
void addDictKeyHandler(
std::string const& key, std::shared_ptr<JSONHandler>);
QPDF_DLL
void addFallbackDictHandler(std::shared_ptr<JSONHandler>);
// Apply the given handler to each element of the array.
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.
QPDF_DLL
@ -108,7 +113,12 @@ class JSONHandler
null_handler(nullptr),
string_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 number_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::shared_ptr<JSONHandler> fallback_dict_handler;
std::shared_ptr<JSONHandler> array_handler;
std::shared_ptr<JSONHandler> array_item_handler;
};
class Members

View File

@ -46,10 +46,18 @@ JSONHandler::addBoolHandler(bool_handler_t fn)
this->m->h.bool_handler = fn;
}
std::map<std::string, std::shared_ptr<JSONHandler>>&
JSONHandler::addDictHandlers()
void
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
@ -59,9 +67,13 @@ JSONHandler::addFallbackDictHandler(std::shared_ptr<JSONHandler> fdh)
}
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
@ -95,9 +107,9 @@ JSONHandler::handle(std::string const& path, JSON j)
this->m->h.bool_handler(path, bvalue);
handled = true;
}
if ((this->m->h.fallback_dict_handler.get() ||
(! this->m->h.dict_handlers.empty())) && j.isDictionary())
if (this->m->h.dict_start_handler && j.isDictionary())
{
this->m->h.dict_start_handler(path);
std::string path_base = path;
if (path_base != ".")
{
@ -126,22 +138,19 @@ JSONHandler::handle(std::string const& path, JSON j)
i->second->handle(path_base + k, v);
}
});
// 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.
this->m->h.dict_end_handler(path);
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;
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);
++i;
});
// Set handled = true even if we didn't call any handlers.
// This could have been an empty array.
this->m->h.array_end_handler(path);
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;
}
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()
{
std::cout << "-- scalar --" << std::endl;
@ -40,41 +47,50 @@ static void test_scalar()
static std::shared_ptr<JSONHandler> make_all_handler()
{
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>();
h1->addStringHandler(print_string);
m["one"] = h1;
h->addDictKeyHandler("one", h1);
auto h2 = std::make_shared<JSONHandler>();
h2->addNumberHandler(print_number);
m["two"] = h2;
h->addDictKeyHandler("two", h2);
auto h3 = std::make_shared<JSONHandler>();
h3->addBoolHandler(print_bool);
m["three"] = h3;
h->addDictKeyHandler("three", h3);
auto h4 = std::make_shared<JSONHandler>();
h4->addAnyHandler(print_json);
m["four"] = h4;
m["phour"] = h4; // share h4
h->addDictKeyHandler("four", h4);
h->addDictKeyHandler("phour", h4); // share h4
auto h5 = std::make_shared<JSONHandler>();
// Allow to be either string or bool
h5->addBoolHandler(print_bool);
h5->addStringHandler(print_string);
h5->addNullHandler(print_null);
auto h5s = std::make_shared<JSONHandler>();
m["five"] = h5s;
h5s->addArrayHandler(h5);
h->addDictKeyHandler("five", h5s);
h5s->addArrayHandlers(
make_print_message("array begin"),
make_print_message("array end"),
h5);
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>();
m6["a"] = h6a;
auto& m6a = h6a->addDictHandlers();
h6->addDictKeyHandler("a", h6a);
h6a->addDictHandlers(
make_print_message("dict begin"),
make_print_message("dict end"));
auto h6ab = std::make_shared<JSONHandler>();
m6a["b"] = h6ab;
h6a->addDictKeyHandler("b", h6ab);
auto h6ax = std::make_shared<JSONHandler>();
h6ax->addAnyHandler(print_json);
h6a->addFallbackDictHandler(h6ax);
m6["b"] = h6ab; // share
h6->addDictKeyHandler("b", h6ab); // share
h6ab->addStringHandler(print_string);
m["six"] = h6;
h->addDictKeyHandler("six", h6);
return h;
}

View File

@ -1,22 +1,31 @@
-- scalar --
.: string: potato
-- all --
.: json: dict begin
.five: json: array begin
.five[0]: string: x
.five[1]: bool: false
.five[2]: string: y
.five[3]: null
.five[4]: bool: true
.five: json: array end
.four: json: [
"a",
1
]
.one: string: potato
.phour: json: null
.six: json: dict begin
.six.a: json: dict begin
.six.a.Q: json: "baaa"
.six.a.b: string: quack
.six.a: json: dict end
.six.b: string: moo
.six: json: dict end
.three: bool: true
.two: number: 3.14
.: json: dict end
-- errors --
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 .