#ifndef XRPL_JSON_WRITER_H_INCLUDED #define XRPL_JSON_WRITER_H_INCLUDED #include #include #include #include #include namespace Json { /** * Writer implements an O(1)-space, O(1)-granular output JSON writer. * * O(1)-space means that it uses a fixed amount of memory, and that there are * no heap allocations at each step of the way. * * O(1)-granular output means the writer only outputs in small segments of a * bounded size, using a bounded number of CPU cycles in doing so. This is * very helpful in scheduling long jobs. * * The tradeoff is that you have to fill items in the JSON tree as you go, * and you can never go backward. * * Writer can write single JSON tokens, but the typical use is to write out an * entire JSON object. For example: * * { * Writer w (out); * * w.startObject (); // Start the root object. * w.set ("hello", "world"); * w.set ("goodbye", 23); * w.finishObject (); // Finish the root object. * } * * which outputs the string * * {"hello":"world","goodbye":23} * * There can be an object inside an object: * * { * Writer w (out); * * w.startObject (); // Start the root object. * w.set ("hello", "world"); * * w.startObjectSet ("subobject"); // Start a sub-object. * w.set ("goodbye", 23); // Add a key, value assignment. * w.finishObject (); // Finish the sub-object. * * w.finishObject (); // Finish the root-object. * } * * which outputs the string * * {"hello":"world","subobject":{"goodbye":23}}. * * Arrays work similarly * * { * Writer w (out); * w.startObject (); // Start the root object. * * w.startArraySet ("hello"); // Start an array. * w.append (23) // Append some items. * w.append ("skidoo") * w.finishArray (); // Finish the array. * * w.finishObject (); // Finish the root object. * } * * which outputs the string * * {"hello":[23,"skidoo"]}. * * * If you've reached the end of a long object, you can just use finishAll() * which finishes all arrays and objects that you have started. * * { * Writer w (out); * w.startObject (); // Start the root object. * * w.startArraySet ("hello"); // Start an array. * w.append (23) // Append an item. * * w.startArrayAppend () // Start a sub-array. * w.append ("one"); * w.append ("two"); * * w.startObjectAppend (); // Append a sub-object. * w.finishAll (); // Finish everything. * } * * which outputs the string * * {"hello":[23,["one","two",{}]]}. * * For convenience, the destructor of Writer calls w.finishAll() which makes * sure that all arrays and objects are closed. This means that you can throw * an exception, or have a coroutine simply clean up the stack, and be sure * that you do in fact generate a complete JSON object. */ class Writer { public: enum CollectionType { array, object }; explicit Writer(Output const& output); Writer(Writer&&) noexcept; Writer& operator=(Writer&&) noexcept; ~Writer(); /** Start a new collection at the root level. */ void startRoot(CollectionType); /** Start a new collection inside an array. */ void startAppend(CollectionType); /** Start a new collection inside an object. */ void startSet(CollectionType, std::string const& key); /** Finish the collection most recently started. */ void finish(); /** Finish all objects and arrays. After finishArray() has been called, no * more operations can be performed. */ void finishAll(); /** Append a value to an array. * * Scalar must be a scalar - that is, a number, boolean, string, string * literal, nullptr or Json::Value */ template void append(Scalar t) { rawAppend(); output(t); } /** Add a comma before this next item if not the first item in an array. Useful if you are writing the actual array yourself. */ void rawAppend(); /** Add a key, value assignment to an object. * * Scalar must be a scalar - that is, a number, boolean, string, string * literal, or nullptr. * * While the JSON spec doesn't explicitly disallow this, you should avoid * calling this method twice with the same tag for the same object. * * If CHECK_JSON_WRITER is defined, this function throws an exception if if * the tag you use has already been used in this object. */ template void set(std::string const& tag, Type t) { rawSet(tag); output(t); } /** Emit just "tag": as part of an object. Useful if you are writing the actual value data yourself. */ void rawSet(std::string const& key); // You won't need to call anything below here until you are writing single // items (numbers, strings, bools, null) to a JSON stream. /*** Output a string. */ void output(std::string const&); /*** Output a literal constant or C string. */ void output(char const*); /*** Output a Json::Value. */ void output(Json::Value const&); /** Output a null. */ void output(std::nullptr_t); /** Output a float. */ void output(float); /** Output a double. */ void output(double); /** Output a bool. */ void output(bool); /** Output numbers or booleans. */ template void output(Type t) { implOutput(std::to_string(t)); } void output(Json::StaticString const& t) { output(t.c_str()); } private: class Impl; std::unique_ptr impl_; void implOutput(std::string const&); }; inline void check(bool condition, std::string const& message) { if (!condition) ripple::Throw(message); } } // namespace Json #endif