#ifndef XRPL_JSON_JSON_WRITER_H_INCLUDED #define XRPL_JSON_JSON_WRITER_H_INCLUDED #include #include #include #include namespace Json { class Value; /** \brief Abstract class for writers. */ class WriterBase { public: virtual ~WriterBase() { } virtual std::string write(Value const& root) = 0; }; /** \brief Outputs a Value in JSON format * without formatting (not human friendly). * * The JSON document is written in a single line. It is not intended for 'human' * consumption, but may be useful to support feature such as RPC where bandwidth * is limited. \sa Reader, Value */ class FastWriter : public WriterBase { public: FastWriter() = default; virtual ~FastWriter() { } public: // overridden from Writer std::string write(Value const& root) override; private: void writeValue(Value const& value); std::string document_; }; /** \brief Writes a Value in JSON format in a * human friendly way. * * The rules for line break and indent are as follow: * - Object value: * - if empty then print {} without indent and line break * - if not empty the print '{', line break & indent, print one value per * line and then unindent and line break and print '}'. * - Array value: * - if empty then print [] without indent and line break * - if the array contains no object value, empty array or some other value * types, and all the values fit on one lines, then print the array on a single * line. * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * * \sa Reader, Value */ class StyledWriter : public WriterBase { public: StyledWriter(); virtual ~StyledWriter() { } public: // overridden from Writer /** \brief Serialize a Value in JSON * format. \param root Value to serialize. \return String containing the * JSON document that represents the root value. */ std::string write(Value const& root) override; private: void writeValue(Value const& value); void writeArrayValue(Value const& value); bool isMultineArray(Value const& value); void pushValue(std::string const& value); void writeIndent(); void writeWithIndent(std::string const& value); void indent(); void unindent(); using ChildValues = std::vector; ChildValues childValues_; std::string document_; std::string indentString_; int rightMargin_; int indentSize_; bool addChildValues_; }; /** \brief Writes a Value in JSON format in a human friendly way, to a stream rather than to a string. * * The rules for line break and indent are as follow: * - Object value: * - if empty then print {} without indent and line break * - if not empty the print '{', line break & indent, print one value per line * and then unindent and line break and print '}'. * - Array value: * - if empty then print [] without indent and line break * - if the array contains no object value, empty array or some other value types, * and all the values fit on one lines, then print the array on a single line. * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * * \param indentation Each level will be indented by this amount extra. * \sa Reader, Value */ class StyledStreamWriter { public: StyledStreamWriter(std::string indentation = "\t"); ~StyledStreamWriter() { } public: /** \brief Serialize a Value in JSON * format. \param out Stream to write to. (Can be ostringstream, e.g.) * \param root Value to serialize. * \note There is no point in deriving from Writer, since write() should not * return a value. */ void write(std::ostream& out, Value const& root); private: void writeValue(Value const& value); void writeArrayValue(Value const& value); bool isMultineArray(Value const& value); void pushValue(std::string const& value); void writeIndent(); void writeWithIndent(std::string const& value); void indent(); void unindent(); using ChildValues = std::vector; ChildValues childValues_; std::ostream* document_; std::string indentString_; int rightMargin_; std::string indentation_; bool addChildValues_; }; std::string valueToString(Int value); std::string valueToString(UInt value); std::string valueToString(double value); std::string valueToString(bool value); std::string valueToQuotedString(char const* value); /// \brief Output using the StyledStreamWriter. /// \see Json::operator>>() std::ostream& operator<<(std::ostream&, Value const& root); //------------------------------------------------------------------------------ // Helpers for stream namespace detail { template void write_string(Write const& write, std::string const& s) { write(s.data(), s.size()); } template void write_value(Write const& write, Value const& value) { switch (value.type()) { case nullValue: write("null", 4); break; case intValue: write_string(write, valueToString(value.asInt())); break; case uintValue: write_string(write, valueToString(value.asUInt())); break; case realValue: write_string(write, valueToString(value.asDouble())); break; case stringValue: write_string(write, valueToQuotedString(value.asCString())); break; case booleanValue: write_string(write, valueToString(value.asBool())); break; case arrayValue: { write("[", 1); int const size = value.size(); for (int index = 0; index < size; ++index) { if (index > 0) write(",", 1); write_value(write, value[index]); } write("]", 1); break; } case objectValue: { Value::Members const members = value.getMemberNames(); write("{", 1); for (auto it = members.begin(); it != members.end(); ++it) { std::string const& name = *it; if (it != members.begin()) write(",", 1); write_string(write, valueToQuotedString(name.c_str())); write(":", 1); write_value(write, value[name]); } write("}", 1); break; } } } } // namespace detail /** Stream compact JSON to the specified function. @param jv The Json::Value to write @param write Invocable with signature void(void const*, std::size_t) that is called when output should be written to the stream. */ template void stream(Json::Value const& jv, Write const& write) { detail::write_value(write, jv); write("\n", 1); } /** Decorator for streaming out compact json Use Json::Value jv; out << Json::Compact{jv} to write a single-line, compact version of `jv` to the stream, rather than the styled format that comes from undecorated streaming. */ class Compact { Json::Value jv_; public: /** Wrap a Json::Value for compact streaming @param jv The Json::Value to stream @note For now, we do not support wrapping lvalues to avoid potentially costly copies. If we find a need, we can consider adding support for compact lvalue streaming in the future. */ Compact(Json::Value&& jv) : jv_{std::move(jv)} { } friend std::ostream& operator<<(std::ostream& o, Compact const& cJv) { detail::write_value( [&o](void const* data, std::size_t n) { o.write(static_cast(data), n); }, cJv.jv_); return o; } }; } // namespace Json #endif // JSON_WRITER_H_INCLUDED