mirror of
https://github.com/Icinga/icinga2.git
synced 2025-09-19 15:57:49 +02:00
Replacing invalid UTF-8 characters beforehand by our selves doesn't make any sense, the serializer can literally perform the same replacement ops with the exact same Unicode replacement character (U+FFFD) on its own. So, why not just use it directly? Instead of wasting memory on a temporary `String` object to always UTF-8 validate every and each value, we just use the serializer to directly to dump the replaced char (if any) into the output writer. No memory waste, no fuss!
118 lines
5.0 KiB
C++
118 lines
5.0 KiB
C++
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
|
|
|
#ifndef JSON_H
|
|
#define JSON_H
|
|
|
|
#include "base/i2-base.hpp"
|
|
#include "base/array.hpp"
|
|
#include "base/generator.hpp"
|
|
#include <boost/asio/spawn.hpp>
|
|
#include <json.hpp>
|
|
|
|
namespace icinga
|
|
{
|
|
|
|
/**
|
|
* AsyncJsonWriter allows writing JSON data to any output stream asynchronously.
|
|
*
|
|
* All users of this class must ensure that the underlying output stream will not perform any asynchronous I/O
|
|
* operations when the @c write_character() or @c write_characters() methods are called. They shall only perform
|
|
* such ops when the @c JsonEncoder allows them to do so by calling the @c Flush() method.
|
|
*
|
|
* @ingroup base
|
|
*/
|
|
class AsyncJsonWriter : public nlohmann::detail::output_adapter_protocol<char>
|
|
{
|
|
public:
|
|
/**
|
|
* Flush instructs the underlying output stream to write any buffered data to wherever it is supposed to go.
|
|
*
|
|
* The @c JsonEncoder allows the stream to even perform asynchronous operations in a safe manner by calling
|
|
* this method with a dedicated @c boost::asio::yield_context object. The stream must not perform any async
|
|
* I/O operations triggered by methods other than this one. Any attempt to do so will result in undefined behavior.
|
|
*
|
|
* However, this doesn't necessarily enforce the stream to really flush its data immediately, but it's up
|
|
* to the implementation to do whatever it needs to. The encoder just gives it a chance to do so by calling
|
|
* this method.
|
|
*
|
|
* @param yield The yield context to use for asynchronous operations.
|
|
*/
|
|
virtual void Flush(boost::asio::yield_context& yield) = 0;
|
|
};
|
|
|
|
class String;
|
|
class Value;
|
|
|
|
/**
|
|
* JSON encoder.
|
|
*
|
|
* This class can be used to encode Icinga Value types into JSON format and write them to an output stream.
|
|
* The supported stream types include any @c std::ostream like objects and our own @c AsyncJsonWriter, which
|
|
* allows writing JSON data to an Asio stream asynchronously. The nlohmann/json library already provides
|
|
* full support for the former stream type, while the latter is fully implemented by our own and satisfies the
|
|
* @c nlohmann::detail::output_adapter_protocol<> interface as well. Therefore, any concrete implementation of
|
|
* @c AsyncJsonWriter may be used to write the produced JSON directly to an Asio either TCP or TLS stream without
|
|
* any additional buffering other than the one used by the Asio buffered_stream<> class internally.
|
|
*
|
|
* The JSON encoder generates most of the low level JSON tokens, but it still relies on the already existing
|
|
* @c nlohmann::detail::serializer<> class to dump numbers and ASCII validated JSON strings. This means that the
|
|
* encoder doesn't perform any kind of JSON validation or escaping on its own, but simply delegates all this kind
|
|
* of work to serializer<>.
|
|
*
|
|
* The generated JSON can be either prettified or compact, depending on your needs. The prettified JSON object
|
|
* is indented with 4 spaces and grows linearly with the depth of the object tree.
|
|
*
|
|
* @ingroup base
|
|
*/
|
|
class JsonEncoder
|
|
{
|
|
public:
|
|
explicit JsonEncoder(std::string& output, bool prettify = false);
|
|
explicit JsonEncoder(std::basic_ostream<char>& stream, bool prettify = false);
|
|
explicit JsonEncoder(nlohmann::detail::output_adapter_t<char> w, bool prettify = false);
|
|
|
|
void Encode(const Value& value, boost::asio::yield_context* yc = nullptr);
|
|
|
|
private:
|
|
void EncodeArray(const Array::Ptr& array, boost::asio::yield_context* yc);
|
|
void EncodeValueGenerator(const ValueGenerator::Ptr& generator, boost::asio::yield_context* yc);
|
|
|
|
template<typename Iterable, typename ValExtractor>
|
|
void EncodeObject(const Iterable& container, const ValExtractor& extractor, boost::asio::yield_context* yc);
|
|
|
|
void EncodeNlohmannJson(const nlohmann::json& json) const;
|
|
void EncodeNumber(double value) const;
|
|
|
|
void FlushIfSafe(boost::asio::yield_context* yc) const;
|
|
|
|
void Write(const std::string_view& sv) const;
|
|
void BeginContainer(char openChar);
|
|
void EndContainer(char closeChar, bool isContainerEmpty = false);
|
|
void WriteSeparatorAndIndentStrIfNeeded(bool emitComma) const;
|
|
|
|
// The number of spaces to use for indentation in prettified JSON.
|
|
static constexpr uint8_t m_IndentSize = 4;
|
|
|
|
const bool m_IsAsyncWriter; // Whether the writer is an instance of AsyncJsonWriter.
|
|
bool m_Pretty; // Whether to pretty-print the JSON output.
|
|
unsigned m_Indent{0}; // The current indentation level for pretty-printing.
|
|
/**
|
|
* Pre-allocate for 8 levels of indentation for pretty-printing.
|
|
*
|
|
* This is used to avoid reallocating the string on every indent level change.
|
|
* The size of this string is dynamically adjusted if the indentation level exceeds its initial size at some point.
|
|
*/
|
|
std::string m_IndentStr{8*m_IndentSize, ' '};
|
|
|
|
// The output stream adapter for writing JSON data. This can be either a std::ostream or an Asio stream adapter.
|
|
nlohmann::detail::output_adapter_t<char> m_Writer;
|
|
};
|
|
|
|
String JsonEncode(const Value& value, bool prettify = false);
|
|
void JsonEncode(const Value& value, std::ostream& os, bool prettify = false);
|
|
Value JsonDecode(const String& data);
|
|
|
|
}
|
|
|
|
#endif /* JSON_H */
|