diff --git a/doc/ref/cbor/cbor.md b/doc/ref/cbor/cbor.md index 1d1b24e92e..6ac34be9d6 100644 --- a/doc/ref/cbor/cbor.md +++ b/doc/ref/cbor/cbor.md @@ -98,6 +98,8 @@ byte_string | base16 | byte string | 23 (Expected conversion to base array | | array |  object | | map |  +## Examples + ### Working with CBOR data For the examples below you need to include some header files and construct a buffer of CBOR data: @@ -282,9 +284,7 @@ end_array (n/a) end_array (n/a) ``` -### More Examples - -#### CBOR and basic_json +### CBOR and basic_json ```c++ #include @@ -361,7 +361,7 @@ Marilyn C, 0.9 (3) Marilyn C ``` -#### Query CBOR with JSONPath +### Query CBOR with JSONPath ```c++ #include #include diff --git a/doc/ref/cbor/cbor_cursor.md b/doc/ref/cbor/cbor_cursor.md index fb6ccfb8a8..1d4210374f 100644 --- a/doc/ref/cbor/cbor_cursor.md +++ b/doc/ref/cbor/cbor_cursor.md @@ -32,12 +32,21 @@ cbor_bytes_cursor |basic_cbor_cursor basic_cbor_cursor(Source&& source); // (1) template - basic_cbor_cursor(Source&& source, std::error_code& ec); // (2) + basic_cbor_cursor(Source&& source, + std::function filter); // (2) -Constructor (1) reads from a buffer or stream source and throws a + template + basic_cbor_cursor(Source&& source, std::error_code& ec); // (3) + + template + basic_cbor_cursor(Source&& source, + std::function filter, + std::error_code& ec); // (4) + +Constructor3 (1)-(2) read from a buffer or stream source and throw a [ser_error](ser_error.md) if a parsing error is encountered while processing the initial event. -Constructor (2) reads from a buffer or stream source and sets `ec` +Constructor3 (3)-(4) read from a buffer or stream source and set `ec` if a parsing error is encountered while processing the initial event. Note: It is the programmer's responsibility to ensure that `basic_cbor_cursor` does not outlive any source passed in the constuctor, @@ -117,11 +126,11 @@ int main() { std::ifstream is("book_catalog.json"); - cbor_cursor reader(is); + cbor_cursor cursor(is); - for (; !reader.done(); reader.next()) + for (; !cursor.done(); cursor.next()) { - const auto& event = reader.current(); + const auto& event = cursor.current(); switch (event.event_type()) { case staj_event_type::begin_array: @@ -239,11 +248,11 @@ int main() std::ifstream is("book_catalog.json"); author_filter filter; - cbor_cursor reader(is, filter); + cbor_cursor cursor(is, filter); - for (; !reader.done(); reader.next()) + for (; !cursor.done(); cursor.next()) { - const auto& event = reader.current(); + const auto& event = cursor.current(); switch (event.event_type()) { case staj_event_type::string_value: diff --git a/doc/ref/ubjson/ubjson.md b/doc/ref/ubjson/ubjson.md index 04c7942e9d..8dc40293f0 100644 --- a/doc/ref/ubjson/ubjson.md +++ b/doc/ref/ubjson/ubjson.md @@ -1,4 +1,4 @@ -### ubjson extension +## ubjson extension The ubjson extension implements encode to and decode from the [Universal Binary JSON Specification](http://ubjson.org/) data format. You can either parse into or serialize from a variant-like structure, [basic_json](../basic_json.md), or your own @@ -6,6 +6,8 @@ data structures, using [json_type_traits](../json_type_traits.md). [decode_ubjson](decode_ubjson.md) +[basic_ubjson_cursor](ubjson_cursor.md) + [encode_ubjson](encode_ubjson.md) [basic_ubjson_encoder](basic_ubjson_encoder.md) @@ -26,9 +28,219 @@ byte_string | | array of uint8_t array | | array object | | object -### Examples +## Examples + +### Working with UBJSON data + +For the examples below you need to include some header files and construct a buffer of UBJSON data: + +```c++ +#include +#include +#include +#include +#include + +using namespace jsoncons; // for convenience + +const std::vector data = +{0x5b,0x23,0x55,0x05, // [ # i 5 +0x44, // float64 + 0x40,0x3d,0xf8,0x51,0xeb,0x85,0x1e,0xb8, // 29.97 +0x44, // float64 + 0x40,0x3f,0x21,0x47,0xae,0x14,0x7a,0xe1, // 31.13 +0x64, // float32 + 0x42,0x86,0x00,0x00, // 67.0 +0x44, // float64 + 0x40,0x00,0xe7,0x6c,0x8b,0x43,0x95,0x81, // 2.113 +0x44, // float64 + 0x40,0x37,0xe3,0x8e,0xf3,0x4d,0x6a,0x16 // 23.8889 +}; +``` + +jsoncons allows you to work with the UBJSON data similarly to JSON data: + +- As a variant-like structure, [basic_json](doc/ref/basic_json.md) + +- As a strongly typed C++ data structure + +- As a stream of parse events + +#### As a variant-like structure + +```c++ +int main() +{ + std::cout << std::dec; + std::cout << std::setprecision(15); + + // Parse the UBJSON data into a json value + json j = ubjson::decode_ubjson(data); + + // Pretty print + std::cout << "(1)\n" << pretty_print(j) << "\n\n"; + + // Iterate over rows + std::cout << "(2)\n"; + for (const auto& item : j.array_range()) + { + std::cout << item.as() << " (" << item.tag() << ")\n"; + } + std::cout << "\n"; + + // Select all values less than 30 with JSONPath + std::cout << "(3)\n"; + json result = jsonpath::json_query(j,"$[?(@ < 30)]"); + std::cout << pretty_print(result) << "\n"; +} +``` +Output: +``` +(1) +[ + 29.97, + 31.13, + 67.0, + 2.113, + 23.8889 +] + +(2) +29.97 (n/a) +31.13 (n/a) +67 (n/a) +2.113 (n/a) +23.8889 (n/a) + +(3) +[ + 29.97, + 2.113, + 23.8889 +] +``` + +#### As a strongly typed C++ data structure + +```c++ +int main() +{ + // Parse the UBJSON data into a std::vector value + auto val = ubjson::decode_ubjson>(data); + + for (auto item : val) + { + std::cout << item << "\n"; + } +} +``` +Output: +``` +29.97 +31.13 +67 +2.113 +23.8889 +``` + +#### As a stream of parse events + +```c++ +int main() +{ + ubjson::ubjson_bytes_cursor cursor(data); + for (; !cursor.done(); cursor.next()) + { + const auto& event = cursor.current(); + switch (event.event_type()) + { + case staj_event_type::begin_array: + std::cout << event.event_type() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::end_array: + std::cout << event.event_type() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::begin_object: + std::cout << event.event_type() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::end_object: + std::cout << event.event_type() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::name: + // Or std::string_view, if supported + std::cout << event.event_type() << ": " << event.get() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::string_value: + // Or std::string_view, if supported + std::cout << event.event_type() << ": " << event.get() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::null_value: + std::cout << event.event_type() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::bool_value: + std::cout << event.event_type() << ": " << std::boolalpha << event.get() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::int64_value: + std::cout << event.event_type() << ": " << event.get() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::uint64_value: + std::cout << event.event_type() << ": " << event.get() << " " << "(" << event.tag() << ")\n"; + break; + case staj_event_type::double_value: + std::cout << event.event_type() << ": " << event.get() << " " << "(" << event.tag() << ")\n"; + break; + default: + std::cout << "Unhandled event type " << event.event_type() << " " << "(" << event.tag() << ")\n"; + break; + } + } +} +``` +Output: +``` +begin_array (n/a) +double_value: 29.97 (n/a) +double_value: 31.13 (n/a) +double_value: 67 (n/a) +double_value: 2.113 (n/a) +double_value: 23.8889 (n/a) +end_array (n/a) +``` + +You can apply a filter to the stream, for example, + +```c++ +int main() +{ + auto filter = [&](const staj_event& ev, const ser_context&) -> bool + { + return (ev.event_type() == staj_event_type::double_value) && (ev.get() < 30.0); + }; + + ubjson::ubjson_bytes_cursor cursor(data, filter); + for (; !cursor.done(); cursor.next()) + { + const auto& event = cursor.current(); + switch (event.event_type()) + { + case staj_event_type::double_value: + std::cout << event.event_type() << ": " << event.get() << " " << "(" << event.tag() << ")\n"; + break; + default: + std::cout << "Unhandled event type " << event.event_type() << " " << "(" << event.tag() << ")\n"; + break; + } + } +} +``` +Output: +``` +double_value: 29.97 (n/a) +double_value: 2.113 (n/a) +double_value: 23.8889 (n/a) +``` -#### encode/decode UBJSON from/to basic_json +### encode/decode UBJSON from/to basic_json ```c++ #include @@ -105,7 +317,7 @@ Marilyn C, 0.9 (3) Marilyn C ``` -#### encode/decode UBJSON from/to your own data structures +### encode/decode UBJSON from/to your own data structures ```c++ #include diff --git a/doc/ref/ubjson/ubjson_cursor.md b/doc/ref/ubjson/ubjson_cursor.md new file mode 100644 index 0000000000..454e19f946 --- /dev/null +++ b/doc/ref/ubjson/ubjson_cursor.md @@ -0,0 +1,96 @@ +### jsoncons::ubjson::basic_ubjson_cursor + +```c++ +#include + +template< + class Src=jsoncons::binary_stream_source, + class Allocator=std::allocator> +class basic_ubjson_cursor; +``` + +A pull parser for reporting UBJSON parse events. A typical application will +repeatedly process the `current()` event and call the `next()` +function to advance to the next event, until `done()` returns `true`. + +`basic_ubjson_cursor` is noncopyable and nonmoveable. + +Typedefs for common sources are provided: + +Type |Definition +--------------------|------------------------------ +ubjson_stream_cursor |basic_ubjson_cursor +ubjson_bytes_cursor |basic_ubjson_cursor + +### Implemented interfaces + +[staj_reader](staj_reader.md) + +#### Constructors + + template + basic_ubjson_cursor(Source&& source); // (1) + + template + basic_ubjson_cursor(Source&& source, + std::function filter); // (2) + + template + basic_ubjson_cursor(Source&& source, std::error_code& ec); // (3) + + template + basic_ubjson_cursor(Source&& source, + std::function filter, + std::error_code& ec); // (4) + +Constructor3 (1)-(2) read from a buffer or stream source and throw a +[ser_error](ser_error.md) if a parsing error is encountered while processing the initial event. + +Constructor3 (3)-(4) read from a buffer or stream source and set `ec` +if a parsing error is encountered while processing the initial event. + +Note: It is the programmer's responsibility to ensure that `basic_ubjson_cursor` does not outlive any source passed in the constuctor, +as `basic_ubjson_cursor` holds pointers to but does not own these resources. + +#### Parameters + +`source` - a value from which a `source_type` is constructible. + +#### Member functions + + bool done() const override; +Checks if there are no more events. + + const staj_event& current() const override; +Returns the current [staj_event](staj_event.md). + + void read_to(json_content_handler& handler) override +Sends the parse events from the current event to the +matching completion event to the supplied [handler](json_content_handler.md) +E.g., if the current event is `begin_object`, sends the `begin_object` +event and all inbetween events until the matching `end_object` event. +If a parsing error is encountered, throws a [ser_error](ser_error.md). + + void read_to(json_content_handler& handler, std::error_code& ec) override +Sends the parse events from the current event to the +matching completion event to the supplied [handler](json_content_handler.md) +E.g., if the current event is `begin_object`, sends the `begin_object` +event and all inbetween events until the matching `end_object` event. +If a parsing error is encountered, sets `ec`. + + void next() override; +Advances to the next event. If a parsing error is encountered, throws a +[ser_error](ser_error.md). + + void next(std::error_code& ec) override; +Advances to the next event. If a parsing error is encountered, sets `ec`. + + const ser_context& context() const override; +Returns the current [context](ser_context.md) + +#### See also + +- [staj_reader](staj_reader.md) +- [staj_array_iterator](staj_array_iterator.md) +- [staj_object_iterator](staj_object_iterator.md) + diff --git a/include/jsoncons_ext/cbor/cbor_cursor.hpp b/include/jsoncons_ext/cbor/cbor_cursor.hpp index 0135150f91..b568b77e63 100644 --- a/include/jsoncons_ext/cbor/cbor_cursor.hpp +++ b/include/jsoncons_ext/cbor/cbor_cursor.hpp @@ -43,7 +43,6 @@ class basic_cbor_cursor : public basic_staj_reader, private virtual ser_co public: typedef string_view string_view_type; - template basic_cbor_cursor(Source&& source) : parser_(std::forward(source)), @@ -81,6 +80,19 @@ class basic_cbor_cursor : public basic_staj_reader, private virtual ser_co } } + template + basic_cbor_cursor(Source&& source, + std::function filter, + std::error_code& ec) + : parser_(std::forward(source)), event_handler_(filter), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + bool done() const override { return parser_.done(); diff --git a/include/jsoncons_ext/ubjson/ubjson_cursor.hpp b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp index 27a5f8fc8d..ae817cf277 100644 --- a/include/jsoncons_ext/ubjson/ubjson_cursor.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp @@ -80,6 +80,19 @@ class basic_ubjson_cursor : public basic_staj_reader, private virtual ser_ } } + template + basic_ubjson_cursor(Source&& source, + std::function filter, + std::error_code& ec) + : parser_(std::forward(source)), event_handler_(filter), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + bool done() const override { return parser_.done();