Commit 1db0a7ffcee6f6ae6bd3298a960665378d304fa1
1 parent
acf8d18b
JSONHandler: rework dictionary and array handlers
Showing
4 changed files
with
89 additions
and
40 deletions
include/qpdf/JSONHandler.hh
| ... | ... | @@ -31,6 +31,11 @@ |
| 31 | 31 | #include <stdexcept> |
| 32 | 32 | #include <memory> |
| 33 | 33 | |
| 34 | +// This class allows a sax-like walk through a JSON object with | |
| 35 | +// functionality that mostly mirrors QPDFArgParser. It is primarily | |
| 36 | +// here to facilitate automatic generation of some of the code to help | |
| 37 | +// keep QPDFJob json consistent with command-line arguments. | |
| 38 | + | |
| 34 | 39 | class JSONHandler |
| 35 | 40 | { |
| 36 | 41 | public: |
| ... | ... | @@ -53,7 +58,8 @@ class JSONHandler |
| 53 | 58 | // certain type. JSONHandler::Error is thrown otherwise. Multiple |
| 54 | 59 | // handlers may be registered, which allows the object to be of |
| 55 | 60 | // various types. If an anyHandler is added, no other handler will |
| 56 | - // be called. | |
| 61 | + // be called. There is no "final" handler -- if the top-level is a | |
| 62 | + // dictionary or array, just use its end handler. | |
| 57 | 63 | |
| 58 | 64 | typedef std::function<void( |
| 59 | 65 | std::string const& path, JSON value)> json_handler_t; |
| ... | ... | @@ -80,19 +86,18 @@ class JSONHandler |
| 80 | 86 | QPDF_DLL |
| 81 | 87 | void addBoolHandler(bool_handler_t fn); |
| 82 | 88 | |
| 83 | - // Returns a reference to a map: keys are expected object keys, | |
| 84 | - // and values are handlers for that object. | |
| 85 | 89 | QPDF_DLL |
| 86 | - std::map<std::string, std::shared_ptr<JSONHandler>>& addDictHandlers(); | |
| 87 | - | |
| 88 | - // Apply the given handler to any key not explicitly in dict | |
| 89 | - // handlers. | |
| 90 | + void addDictHandlers(void_handler_t start_fn, void_handler_t end_fn); | |
| 91 | + QPDF_DLL | |
| 92 | + void addDictKeyHandler( | |
| 93 | + std::string const& key, std::shared_ptr<JSONHandler>); | |
| 90 | 94 | QPDF_DLL |
| 91 | 95 | void addFallbackDictHandler(std::shared_ptr<JSONHandler>); |
| 92 | 96 | |
| 93 | - // Apply the given handler to each element of the array. | |
| 94 | 97 | QPDF_DLL |
| 95 | - void addArrayHandler(std::shared_ptr<JSONHandler>); | |
| 98 | + void addArrayHandlers(void_handler_t start_fn, | |
| 99 | + void_handler_t end_fn, | |
| 100 | + std::shared_ptr<JSONHandler> item_handlers); | |
| 96 | 101 | |
| 97 | 102 | // Apply handlers recursively to a JSON object. |
| 98 | 103 | QPDF_DLL |
| ... | ... | @@ -108,7 +113,12 @@ class JSONHandler |
| 108 | 113 | null_handler(nullptr), |
| 109 | 114 | string_handler(nullptr), |
| 110 | 115 | number_handler(nullptr), |
| 111 | - bool_handler(nullptr) | |
| 116 | + bool_handler(nullptr), | |
| 117 | + dict_start_handler(nullptr), | |
| 118 | + dict_end_handler(nullptr), | |
| 119 | + array_start_handler(nullptr), | |
| 120 | + array_end_handler(nullptr), | |
| 121 | + final_handler(nullptr) | |
| 112 | 122 | { |
| 113 | 123 | } |
| 114 | 124 | |
| ... | ... | @@ -117,9 +127,14 @@ class JSONHandler |
| 117 | 127 | string_handler_t string_handler; |
| 118 | 128 | string_handler_t number_handler; |
| 119 | 129 | bool_handler_t bool_handler; |
| 130 | + void_handler_t dict_start_handler; | |
| 131 | + void_handler_t dict_end_handler; | |
| 132 | + void_handler_t array_start_handler; | |
| 133 | + void_handler_t array_end_handler; | |
| 134 | + void_handler_t final_handler; | |
| 120 | 135 | std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers; |
| 121 | 136 | std::shared_ptr<JSONHandler> fallback_dict_handler; |
| 122 | - std::shared_ptr<JSONHandler> array_handler; | |
| 137 | + std::shared_ptr<JSONHandler> array_item_handler; | |
| 123 | 138 | }; |
| 124 | 139 | |
| 125 | 140 | class Members | ... | ... |
libqpdf/JSONHandler.cc
| ... | ... | @@ -46,10 +46,18 @@ JSONHandler::addBoolHandler(bool_handler_t fn) |
| 46 | 46 | this->m->h.bool_handler = fn; |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | -std::map<std::string, std::shared_ptr<JSONHandler>>& | |
| 50 | -JSONHandler::addDictHandlers() | |
| 49 | +void | |
| 50 | +JSONHandler::addDictHandlers(void_handler_t start_fn, void_handler_t end_fn) | |
| 51 | +{ | |
| 52 | + this->m->h.dict_start_handler = start_fn; | |
| 53 | + this->m->h.dict_end_handler = end_fn; | |
| 54 | +} | |
| 55 | + | |
| 56 | +void | |
| 57 | +JSONHandler::addDictKeyHandler( | |
| 58 | + std::string const& key, std::shared_ptr<JSONHandler> dkh) | |
| 51 | 59 | { |
| 52 | - return this->m->h.dict_handlers; | |
| 60 | + this->m->h.dict_handlers[key] = dkh; | |
| 53 | 61 | } |
| 54 | 62 | |
| 55 | 63 | void |
| ... | ... | @@ -59,9 +67,13 @@ JSONHandler::addFallbackDictHandler(std::shared_ptr<JSONHandler> fdh) |
| 59 | 67 | } |
| 60 | 68 | |
| 61 | 69 | void |
| 62 | -JSONHandler::addArrayHandler(std::shared_ptr<JSONHandler> ah) | |
| 70 | +JSONHandler::addArrayHandlers(void_handler_t start_fn, | |
| 71 | + void_handler_t end_fn, | |
| 72 | + std::shared_ptr<JSONHandler> ah) | |
| 63 | 73 | { |
| 64 | - this->m->h.array_handler = ah; | |
| 74 | + this->m->h.array_start_handler = start_fn; | |
| 75 | + this->m->h.array_end_handler = end_fn; | |
| 76 | + this->m->h.array_item_handler = ah; | |
| 65 | 77 | } |
| 66 | 78 | |
| 67 | 79 | void |
| ... | ... | @@ -95,9 +107,9 @@ JSONHandler::handle(std::string const& path, JSON j) |
| 95 | 107 | this->m->h.bool_handler(path, bvalue); |
| 96 | 108 | handled = true; |
| 97 | 109 | } |
| 98 | - if ((this->m->h.fallback_dict_handler.get() || | |
| 99 | - (! this->m->h.dict_handlers.empty())) && j.isDictionary()) | |
| 110 | + if (this->m->h.dict_start_handler && j.isDictionary()) | |
| 100 | 111 | { |
| 112 | + this->m->h.dict_start_handler(path); | |
| 101 | 113 | std::string path_base = path; |
| 102 | 114 | if (path_base != ".") |
| 103 | 115 | { |
| ... | ... | @@ -126,22 +138,19 @@ JSONHandler::handle(std::string const& path, JSON j) |
| 126 | 138 | i->second->handle(path_base + k, v); |
| 127 | 139 | } |
| 128 | 140 | }); |
| 129 | - | |
| 130 | - // Set handled = true even if we didn't call any handlers. | |
| 131 | - // This dictionary could have been empty, but it's okay since | |
| 132 | - // it's a dictionary like it's supposed to be. | |
| 141 | + this->m->h.dict_end_handler(path); | |
| 133 | 142 | handled = true; |
| 134 | 143 | } |
| 135 | - if (this->m->h.array_handler.get()) | |
| 144 | + if (this->m->h.array_start_handler && j.isArray()) | |
| 136 | 145 | { |
| 146 | + this->m->h.array_start_handler(path); | |
| 137 | 147 | size_t i = 0; |
| 138 | 148 | j.forEachArrayItem([&i, &path, this](JSON v) { |
| 139 | - this->m->h.array_handler->handle( | |
| 149 | + this->m->h.array_item_handler->handle( | |
| 140 | 150 | path + "[" + QUtil::uint_to_string(i) + "]", v); |
| 141 | 151 | ++i; |
| 142 | 152 | }); |
| 143 | - // Set handled = true even if we didn't call any handlers. | |
| 144 | - // This could have been an empty array. | |
| 153 | + this->m->h.array_end_handler(path); | |
| 145 | 154 | handled = true; |
| 146 | 155 | } |
| 147 | 156 | ... | ... |
libtests/json_handler.cc
| ... | ... | @@ -28,6 +28,13 @@ static void print_json(std::string const& path, JSON value) |
| 28 | 28 | std::cout << path << ": json: " << value.unparse() << std::endl; |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | +static JSONHandler::void_handler_t make_print_message(std::string msg) | |
| 32 | +{ | |
| 33 | + return [msg](std::string const& path) { | |
| 34 | + std::cout << path << ": json: " << msg << std::endl; | |
| 35 | + }; | |
| 36 | +} | |
| 37 | + | |
| 31 | 38 | static void test_scalar() |
| 32 | 39 | { |
| 33 | 40 | std::cout << "-- scalar --" << std::endl; |
| ... | ... | @@ -40,41 +47,50 @@ static void test_scalar() |
| 40 | 47 | static std::shared_ptr<JSONHandler> make_all_handler() |
| 41 | 48 | { |
| 42 | 49 | auto h = std::make_shared<JSONHandler>(); |
| 43 | - auto& m = h->addDictHandlers(); | |
| 50 | + h->addDictHandlers( | |
| 51 | + make_print_message("dict begin"), | |
| 52 | + make_print_message("dict end")); | |
| 44 | 53 | auto h1 = std::make_shared<JSONHandler>(); |
| 45 | 54 | h1->addStringHandler(print_string); |
| 46 | - m["one"] = h1; | |
| 55 | + h->addDictKeyHandler("one", h1); | |
| 47 | 56 | auto h2 = std::make_shared<JSONHandler>(); |
| 48 | 57 | h2->addNumberHandler(print_number); |
| 49 | - m["two"] = h2; | |
| 58 | + h->addDictKeyHandler("two", h2); | |
| 50 | 59 | auto h3 = std::make_shared<JSONHandler>(); |
| 51 | 60 | h3->addBoolHandler(print_bool); |
| 52 | - m["three"] = h3; | |
| 61 | + h->addDictKeyHandler("three", h3); | |
| 53 | 62 | auto h4 = std::make_shared<JSONHandler>(); |
| 54 | 63 | h4->addAnyHandler(print_json); |
| 55 | - m["four"] = h4; | |
| 56 | - m["phour"] = h4; // share h4 | |
| 64 | + h->addDictKeyHandler("four", h4); | |
| 65 | + h->addDictKeyHandler("phour", h4); // share h4 | |
| 57 | 66 | auto h5 = std::make_shared<JSONHandler>(); |
| 58 | 67 | // Allow to be either string or bool |
| 59 | 68 | h5->addBoolHandler(print_bool); |
| 60 | 69 | h5->addStringHandler(print_string); |
| 61 | 70 | h5->addNullHandler(print_null); |
| 62 | 71 | auto h5s = std::make_shared<JSONHandler>(); |
| 63 | - m["five"] = h5s; | |
| 64 | - h5s->addArrayHandler(h5); | |
| 72 | + h->addDictKeyHandler("five", h5s); | |
| 73 | + h5s->addArrayHandlers( | |
| 74 | + make_print_message("array begin"), | |
| 75 | + make_print_message("array end"), | |
| 76 | + h5); | |
| 65 | 77 | auto h6 = std::make_shared<JSONHandler>(); |
| 66 | - auto& m6 = h6->addDictHandlers(); | |
| 78 | + h6->addDictHandlers( | |
| 79 | + make_print_message("dict begin"), | |
| 80 | + make_print_message("dict end")); | |
| 67 | 81 | auto h6a = std::make_shared<JSONHandler>(); |
| 68 | - m6["a"] = h6a; | |
| 69 | - auto& m6a = h6a->addDictHandlers(); | |
| 82 | + h6->addDictKeyHandler("a", h6a); | |
| 83 | + h6a->addDictHandlers( | |
| 84 | + make_print_message("dict begin"), | |
| 85 | + make_print_message("dict end")); | |
| 70 | 86 | auto h6ab = std::make_shared<JSONHandler>(); |
| 71 | - m6a["b"] = h6ab; | |
| 87 | + h6a->addDictKeyHandler("b", h6ab); | |
| 72 | 88 | auto h6ax = std::make_shared<JSONHandler>(); |
| 73 | 89 | h6ax->addAnyHandler(print_json); |
| 74 | 90 | h6a->addFallbackDictHandler(h6ax); |
| 75 | - m6["b"] = h6ab; // share | |
| 91 | + h6->addDictKeyHandler("b", h6ab); // share | |
| 76 | 92 | h6ab->addStringHandler(print_string); |
| 77 | - m["six"] = h6; | |
| 93 | + h->addDictKeyHandler("six", h6); | |
| 78 | 94 | return h; |
| 79 | 95 | } |
| 80 | 96 | ... | ... |
libtests/qtest/json_handler/json_handler.out
| 1 | 1 | -- scalar -- |
| 2 | 2 | .: string: potato |
| 3 | 3 | -- all -- |
| 4 | +.: json: dict begin | |
| 5 | +.five: json: array begin | |
| 4 | 6 | .five[0]: string: x |
| 5 | 7 | .five[1]: bool: false |
| 6 | 8 | .five[2]: string: y |
| 7 | 9 | .five[3]: null |
| 8 | 10 | .five[4]: bool: true |
| 11 | +.five: json: array end | |
| 9 | 12 | .four: json: [ |
| 10 | 13 | "a", |
| 11 | 14 | 1 |
| 12 | 15 | ] |
| 13 | 16 | .one: string: potato |
| 14 | 17 | .phour: json: null |
| 18 | +.six: json: dict begin | |
| 19 | +.six.a: json: dict begin | |
| 15 | 20 | .six.a.Q: json: "baaa" |
| 16 | 21 | .six.a.b: string: quack |
| 22 | +.six.a: json: dict end | |
| 17 | 23 | .six.b: string: moo |
| 24 | +.six: json: dict end | |
| 18 | 25 | .three: bool: true |
| 19 | 26 | .two: number: 3.14 |
| 27 | +.: json: dict end | |
| 20 | 28 | -- errors -- |
| 21 | 29 | bad type at top: JSON handler: value at . is not of expected type |
| 30 | +.: json: dict begin | |
| 22 | 31 | unexpected key: JSON handler found unexpected key x in object at . | ... | ... |