Refactor: fancy_serializer tracks a json_pointer context
This commit is contained in:
parent
91971e394f
commit
ca3f3959a8
@ -22,6 +22,7 @@
|
||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
#include <nlohmann/detail/output/primitive_serializer.hpp>
|
||||
#include <nlohmann/detail/json_pointer.hpp>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
@ -103,6 +104,7 @@ class fancy_serializer
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using json_pointer_t = json_pointer<BasicJsonType>;
|
||||
static constexpr uint8_t UTF8_ACCEPT = 0;
|
||||
static constexpr uint8_t UTF8_REJECT = 1;
|
||||
|
||||
@ -123,7 +125,7 @@ class fancy_serializer
|
||||
|
||||
void dump(const BasicJsonType& val, const bool ensure_ascii)
|
||||
{
|
||||
dump(val, ensure_ascii, 0, &stylizer.get_default_style());
|
||||
dump(val, ensure_ascii, 0, &stylizer.get_default_style(), json_pointer_t());
|
||||
}
|
||||
|
||||
private:
|
||||
@ -146,19 +148,25 @@ class fancy_serializer
|
||||
void dump(const BasicJsonType& val,
|
||||
const bool ensure_ascii,
|
||||
const unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
if (context.cbegin() != context.cend())
|
||||
{
|
||||
active_style = stylizer.get_new_style_or_active(*context.crbegin(), active_style);
|
||||
}
|
||||
|
||||
switch (val.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
dump_object(val, ensure_ascii, depth, active_style);
|
||||
dump_object(val, ensure_ascii, depth, active_style, context);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
dump_array(val, ensure_ascii, depth, active_style);
|
||||
dump_array(val, ensure_ascii, depth, active_style, context);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -217,7 +225,8 @@ class fancy_serializer
|
||||
template <typename Iterator>
|
||||
void dump_object_key_value(
|
||||
Iterator i, bool ensure_ascii, unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
const auto new_indent = (depth + 1) * active_style->indent_step * active_style->multiline;
|
||||
const int newline_len = active_style->space_after_colon;
|
||||
@ -226,14 +235,14 @@ class fancy_serializer
|
||||
o->write_character('\"');
|
||||
prim_serializer.dump_escaped(*o, i->first, ensure_ascii);
|
||||
o->write_characters("\": ", 2 + newline_len);
|
||||
auto new_style = stylizer.get_new_style_or_active(i->first, active_style);
|
||||
dump(i->second, ensure_ascii, depth + 1, new_style);
|
||||
dump(i->second, ensure_ascii, depth + 1, active_style, context.appended(i->first));
|
||||
}
|
||||
|
||||
void dump_object(const BasicJsonType& val,
|
||||
bool ensure_ascii,
|
||||
unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
if (val.m_value.object->empty())
|
||||
{
|
||||
@ -261,14 +270,14 @@ class fancy_serializer
|
||||
auto i = val.m_value.object->cbegin();
|
||||
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
||||
{
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style);
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style, context);
|
||||
o->write_characters(",\n", 1 + newline_len);
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(i != val.m_value.object->cend());
|
||||
assert(std::next(i) == val.m_value.object->cend());
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style);
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style, context);
|
||||
|
||||
o->write_characters("\n", newline_len);
|
||||
o->write_characters(indent_string.c_str(), old_indent);
|
||||
@ -278,7 +287,8 @@ class fancy_serializer
|
||||
void dump_array(const BasicJsonType& val,
|
||||
bool ensure_ascii,
|
||||
unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
if (val.m_value.array->empty())
|
||||
{
|
||||
@ -313,14 +323,16 @@ class fancy_serializer
|
||||
i != val.m_value.array->cend() - 1; ++i)
|
||||
{
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(*i, ensure_ascii, depth + 1, active_style);
|
||||
dump(*i, ensure_ascii, depth + 1, active_style,
|
||||
context.appended(i - val.m_value.array->cbegin()));
|
||||
o->write_characters(comma_string.first, comma_string.second);
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(not val.m_value.array->empty());
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(val.m_value.array->back(), ensure_ascii, depth + 1, active_style);
|
||||
dump(val.m_value.array->back(), ensure_ascii, depth + 1, active_style,
|
||||
context.appended(val.m_value.array->size()));
|
||||
|
||||
o->write_characters("\n", newline_len);
|
||||
o->write_characters(indent_string.c_str(), old_indent);
|
||||
|
||||
@ -10092,474 +10092,6 @@ class serializer
|
||||
|
||||
// #include <nlohmann/detail/output/primitive_serializer.hpp>
|
||||
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
struct fancy_serializer_style
|
||||
{
|
||||
unsigned int indent_step = 4;
|
||||
char indent_char = ' ';
|
||||
|
||||
unsigned int depth_limit = std::numeric_limits<unsigned>::max();
|
||||
|
||||
unsigned int strings_maximum_length = 0;
|
||||
|
||||
bool space_after_colon = false;
|
||||
bool space_after_comma = false;
|
||||
|
||||
bool multiline = false;
|
||||
|
||||
void set_old_multiline()
|
||||
{
|
||||
space_after_colon = space_after_comma = multiline = true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class basic_fancy_serializer_stylizer
|
||||
{
|
||||
public:
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
|
||||
basic_fancy_serializer_stylizer(fancy_serializer_style const& ds)
|
||||
: default_style(ds)
|
||||
{}
|
||||
|
||||
basic_fancy_serializer_stylizer() = default;
|
||||
|
||||
public:
|
||||
const fancy_serializer_style& get_default_style() const
|
||||
{
|
||||
return default_style;
|
||||
}
|
||||
|
||||
fancy_serializer_style& get_default_style()
|
||||
{
|
||||
return default_style;
|
||||
}
|
||||
|
||||
const fancy_serializer_style* get_new_style_or_active(
|
||||
const string_t& j,
|
||||
const fancy_serializer_style* active_style) const
|
||||
{
|
||||
auto iter = key_styles.find(j);
|
||||
return iter == key_styles.end() ? active_style : &iter->second;
|
||||
}
|
||||
|
||||
fancy_serializer_style& get_or_insert_style(const string_t& j)
|
||||
{
|
||||
return key_styles[j];
|
||||
}
|
||||
|
||||
private:
|
||||
fancy_serializer_style default_style;
|
||||
|
||||
std::map<string_t, fancy_serializer_style> key_styles;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
///////////////////
|
||||
// serialization //
|
||||
///////////////////
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class fancy_serializer
|
||||
{
|
||||
using stylizer_t = basic_fancy_serializer_stylizer<BasicJsonType>;
|
||||
using primitive_serializer_t = primitive_serializer<BasicJsonType>;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
static constexpr uint8_t UTF8_ACCEPT = 0;
|
||||
static constexpr uint8_t UTF8_REJECT = 1;
|
||||
|
||||
public:
|
||||
/*!
|
||||
@param[in] s output stream to serialize to
|
||||
@param[in] ichar indentation character to use
|
||||
*/
|
||||
fancy_serializer(output_adapter_t<char> s,
|
||||
const stylizer_t& st)
|
||||
: o(std::move(s)), stylizer(st),
|
||||
indent_string(512, st.get_default_style().indent_char)
|
||||
{}
|
||||
|
||||
// delete because of pointer members
|
||||
fancy_serializer(const fancy_serializer&) = delete;
|
||||
fancy_serializer& operator=(const fancy_serializer&) = delete;
|
||||
|
||||
void dump(const BasicJsonType& val, const bool ensure_ascii)
|
||||
{
|
||||
dump(val, ensure_ascii, 0, &stylizer.get_default_style());
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief internal implementation of the serialization function
|
||||
|
||||
This function is called by the public member function dump and organizes
|
||||
the serialization internally. The indentation level is propagated as
|
||||
additional parameter. In case of arrays and objects, the function is
|
||||
called recursively.
|
||||
|
||||
- strings and object keys are escaped using `escape_string()`
|
||||
- integer numbers are converted implicitly via `operator<<`
|
||||
- floating-point numbers are converted to a string using `"%g"` format
|
||||
|
||||
@param[in] val value to serialize
|
||||
@param[in] pretty_print whether the output shall be pretty-printed
|
||||
@param[in] depth the current recursive depth
|
||||
*/
|
||||
void dump(const BasicJsonType& val,
|
||||
const bool ensure_ascii,
|
||||
const unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
{
|
||||
switch (val.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
dump_object(val, ensure_ascii, depth, active_style);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
dump_array(val, ensure_ascii, depth, active_style);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
dump_string(*val.m_value.string, ensure_ascii, active_style);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::boolean:
|
||||
{
|
||||
if (val.m_value.boolean)
|
||||
{
|
||||
o->write_characters("true", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_characters("false", 5);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
prim_serializer.dump_integer(*o, val.m_value.number_integer);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
prim_serializer.dump_integer(*o, val.m_value.number_unsigned);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
prim_serializer.dump_float(*o, val.m_value.number_float);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::discarded:
|
||||
{
|
||||
o->write_characters("<discarded>", 11);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
o->write_characters("null", 4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Iterator>
|
||||
void dump_object_key_value(
|
||||
Iterator i, bool ensure_ascii, unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
{
|
||||
const auto new_indent = (depth + 1) * active_style->indent_step * active_style->multiline;
|
||||
const int newline_len = active_style->space_after_colon;
|
||||
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
o->write_character('\"');
|
||||
prim_serializer.dump_escaped(*o, i->first, ensure_ascii);
|
||||
o->write_characters("\": ", 2 + newline_len);
|
||||
auto new_style = stylizer.get_new_style_or_active(i->first, active_style);
|
||||
dump(i->second, ensure_ascii, depth + 1, new_style);
|
||||
}
|
||||
|
||||
void dump_object(const BasicJsonType& val,
|
||||
bool ensure_ascii,
|
||||
unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
{
|
||||
if (val.m_value.object->empty())
|
||||
{
|
||||
o->write_characters("{}", 2);
|
||||
return;
|
||||
}
|
||||
else if (depth >= active_style->depth_limit)
|
||||
{
|
||||
o->write_characters("{...}", 5);
|
||||
return;
|
||||
}
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto old_indent = depth * active_style->indent_step * active_style->multiline;
|
||||
const auto new_indent = (depth + 1) * active_style->indent_step * active_style->multiline;
|
||||
if (JSON_UNLIKELY(indent_string.size() < new_indent))
|
||||
{
|
||||
indent_string.resize(indent_string.size() * 2, active_style->indent_char);
|
||||
}
|
||||
const int newline_len = (active_style->multiline ? 1 : 0);
|
||||
|
||||
o->write_characters("{\n", 1 + newline_len);
|
||||
|
||||
// first n-1 elements
|
||||
auto i = val.m_value.object->cbegin();
|
||||
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
||||
{
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style);
|
||||
o->write_characters(",\n", 1 + newline_len);
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(i != val.m_value.object->cend());
|
||||
assert(std::next(i) == val.m_value.object->cend());
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style);
|
||||
|
||||
o->write_characters("\n", newline_len);
|
||||
o->write_characters(indent_string.c_str(), old_indent);
|
||||
o->write_character('}');
|
||||
}
|
||||
|
||||
void dump_array(const BasicJsonType& val,
|
||||
bool ensure_ascii,
|
||||
unsigned int depth,
|
||||
const fancy_serializer_style* active_style)
|
||||
{
|
||||
if (val.m_value.array->empty())
|
||||
{
|
||||
o->write_characters("[]", 2);
|
||||
return;
|
||||
}
|
||||
else if (depth >= active_style->depth_limit)
|
||||
{
|
||||
o->write_characters("[...]", 5);
|
||||
return;
|
||||
}
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto old_indent = depth * active_style->indent_step * active_style->multiline;;
|
||||
const auto new_indent = (depth + 1) * active_style->indent_step * active_style->multiline;;
|
||||
if (JSON_UNLIKELY(indent_string.size() < new_indent))
|
||||
{
|
||||
indent_string.resize(indent_string.size() * 2, active_style->indent_char);
|
||||
}
|
||||
const int newline_len = (active_style->multiline ? 1 : 0);
|
||||
|
||||
using pair = std::pair<const char*, int>;
|
||||
auto comma_string =
|
||||
active_style->multiline ? pair(",\n", 2) :
|
||||
active_style->space_after_comma ? pair(", ", 2) :
|
||||
pair(",", 1);
|
||||
|
||||
o->write_characters("[\n", 1 + newline_len);
|
||||
|
||||
// first n-1 elements
|
||||
for (auto i = val.m_value.array->cbegin();
|
||||
i != val.m_value.array->cend() - 1; ++i)
|
||||
{
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(*i, ensure_ascii, depth + 1, active_style);
|
||||
o->write_characters(comma_string.first, comma_string.second);
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(not val.m_value.array->empty());
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(val.m_value.array->back(), ensure_ascii, depth + 1, active_style);
|
||||
|
||||
o->write_characters("\n", newline_len);
|
||||
o->write_characters(indent_string.c_str(), old_indent);
|
||||
o->write_character(']');
|
||||
}
|
||||
|
||||
void dump_string(const string_t& str, bool ensure_ascii,
|
||||
const fancy_serializer_style* active_style)
|
||||
{
|
||||
o->write_character('\"');
|
||||
if (active_style->strings_maximum_length == 0)
|
||||
{
|
||||
prim_serializer.dump_escaped(*o, str, ensure_ascii);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream ss;
|
||||
nlohmann::detail::output_adapter<char> o_string(ss);
|
||||
nlohmann::detail::output_adapter_t<char> oo_string = o_string;
|
||||
prim_serializer.dump_escaped(*oo_string, str, ensure_ascii);
|
||||
|
||||
std::string full_str = ss.str();
|
||||
if (full_str.size() <= active_style->strings_maximum_length)
|
||||
{
|
||||
o->write_characters(full_str.c_str(), full_str.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned start_len = [](unsigned int maxl)
|
||||
{
|
||||
if (maxl <= 3)
|
||||
{
|
||||
// There is only room for the ellipsis,
|
||||
// no characters from the string
|
||||
return 0u;
|
||||
}
|
||||
else if (maxl <= 5)
|
||||
{
|
||||
// With four allowed characters, we add in the
|
||||
// first from the string. With five, we add in
|
||||
// the *last* instead, so still just one at
|
||||
// the start.
|
||||
return 1u;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We subtract three for the ellipsis
|
||||
// and one for the last character.
|
||||
return maxl - 4;
|
||||
}
|
||||
}(active_style->strings_maximum_length);
|
||||
|
||||
const unsigned end_len =
|
||||
active_style->strings_maximum_length >= 5 ? 1 : 0;
|
||||
|
||||
const unsigned ellipsis_length =
|
||||
active_style->strings_maximum_length >= 3
|
||||
? 3
|
||||
: active_style->strings_maximum_length;
|
||||
|
||||
o->write_characters(full_str.c_str(), start_len);
|
||||
o->write_characters("...", ellipsis_length);
|
||||
o->write_characters(full_str.c_str() + str.size() - end_len, end_len);
|
||||
}
|
||||
}
|
||||
o->write_character('\"');
|
||||
}
|
||||
|
||||
private:
|
||||
/// the output of the fancy_serializer
|
||||
output_adapter_t<char> o = nullptr;
|
||||
|
||||
/// Used for serializing "base" objects. Strings are sort of
|
||||
/// counted in this, but not completely.
|
||||
primitive_serializer_t prim_serializer;
|
||||
|
||||
/// the indentation string
|
||||
string_t indent_string;
|
||||
|
||||
/// Output style
|
||||
const stylizer_t stylizer;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
std::ostream& fancy_dump(std::ostream& o, const BasicJsonType& j,
|
||||
basic_fancy_serializer_stylizer<BasicJsonType> const& stylizer)
|
||||
{
|
||||
// do the actual serialization
|
||||
detail::fancy_serializer<BasicJsonType> s(detail::output_adapter<char>(o), stylizer);
|
||||
s.dump(j, false);
|
||||
return o;
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
std::ostream& fancy_dump(std::ostream& o, const BasicJsonType& j,
|
||||
fancy_serializer_style style)
|
||||
{
|
||||
basic_fancy_serializer_stylizer<BasicJsonType> stylizer(style);
|
||||
return fancy_dump(o, j, stylizer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// #include <nlohmann/detail/json_ref.hpp>
|
||||
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
class json_ref
|
||||
{
|
||||
public:
|
||||
using value_type = BasicJsonType;
|
||||
|
||||
json_ref(value_type&& value)
|
||||
: owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
|
||||
{}
|
||||
|
||||
json_ref(const value_type& value)
|
||||
: value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
|
||||
{}
|
||||
|
||||
json_ref(std::initializer_list<json_ref> init)
|
||||
: owned_value(init), value_ref(&owned_value), is_rvalue(true)
|
||||
{}
|
||||
|
||||
template<class... Args>
|
||||
json_ref(Args&& ... args)
|
||||
: owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true)
|
||||
{}
|
||||
|
||||
// class should be movable only
|
||||
json_ref(json_ref&&) = default;
|
||||
json_ref(const json_ref&) = delete;
|
||||
json_ref& operator=(const json_ref&) = delete;
|
||||
|
||||
value_type moved_or_copied() const
|
||||
{
|
||||
if (is_rvalue)
|
||||
{
|
||||
return std::move(*value_ref);
|
||||
}
|
||||
return *value_ref;
|
||||
}
|
||||
|
||||
value_type const& operator*() const
|
||||
{
|
||||
return *static_cast<value_type const*>(value_ref);
|
||||
}
|
||||
|
||||
value_type const* operator->() const
|
||||
{
|
||||
return static_cast<value_type const*>(value_ref);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable value_type owned_value = nullptr;
|
||||
value_type* value_ref = nullptr;
|
||||
const bool is_rvalue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// #include <nlohmann/detail/json_pointer.hpp>
|
||||
|
||||
|
||||
@ -11296,6 +10828,487 @@ class json_pointer
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
struct fancy_serializer_style
|
||||
{
|
||||
unsigned int indent_step = 4;
|
||||
char indent_char = ' ';
|
||||
|
||||
unsigned int depth_limit = std::numeric_limits<unsigned>::max();
|
||||
|
||||
unsigned int strings_maximum_length = 0;
|
||||
|
||||
bool space_after_colon = false;
|
||||
bool space_after_comma = false;
|
||||
|
||||
bool multiline = false;
|
||||
|
||||
void set_old_multiline()
|
||||
{
|
||||
space_after_colon = space_after_comma = multiline = true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class basic_fancy_serializer_stylizer
|
||||
{
|
||||
public:
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
|
||||
basic_fancy_serializer_stylizer(fancy_serializer_style const& ds)
|
||||
: default_style(ds)
|
||||
{}
|
||||
|
||||
basic_fancy_serializer_stylizer() = default;
|
||||
|
||||
public:
|
||||
const fancy_serializer_style& get_default_style() const
|
||||
{
|
||||
return default_style;
|
||||
}
|
||||
|
||||
fancy_serializer_style& get_default_style()
|
||||
{
|
||||
return default_style;
|
||||
}
|
||||
|
||||
const fancy_serializer_style* get_new_style_or_active(
|
||||
const string_t& j,
|
||||
const fancy_serializer_style* active_style) const
|
||||
{
|
||||
auto iter = key_styles.find(j);
|
||||
return iter == key_styles.end() ? active_style : &iter->second;
|
||||
}
|
||||
|
||||
fancy_serializer_style& get_or_insert_style(const string_t& j)
|
||||
{
|
||||
return key_styles[j];
|
||||
}
|
||||
|
||||
private:
|
||||
fancy_serializer_style default_style;
|
||||
|
||||
std::map<string_t, fancy_serializer_style> key_styles;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
///////////////////
|
||||
// serialization //
|
||||
///////////////////
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class fancy_serializer
|
||||
{
|
||||
using stylizer_t = basic_fancy_serializer_stylizer<BasicJsonType>;
|
||||
using primitive_serializer_t = primitive_serializer<BasicJsonType>;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using json_pointer_t = json_pointer<BasicJsonType>;
|
||||
static constexpr uint8_t UTF8_ACCEPT = 0;
|
||||
static constexpr uint8_t UTF8_REJECT = 1;
|
||||
|
||||
public:
|
||||
/*!
|
||||
@param[in] s output stream to serialize to
|
||||
@param[in] ichar indentation character to use
|
||||
*/
|
||||
fancy_serializer(output_adapter_t<char> s,
|
||||
const stylizer_t& st)
|
||||
: o(std::move(s)), stylizer(st),
|
||||
indent_string(512, st.get_default_style().indent_char)
|
||||
{}
|
||||
|
||||
// delete because of pointer members
|
||||
fancy_serializer(const fancy_serializer&) = delete;
|
||||
fancy_serializer& operator=(const fancy_serializer&) = delete;
|
||||
|
||||
void dump(const BasicJsonType& val, const bool ensure_ascii)
|
||||
{
|
||||
dump(val, ensure_ascii, 0, &stylizer.get_default_style(), json_pointer_t());
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief internal implementation of the serialization function
|
||||
|
||||
This function is called by the public member function dump and organizes
|
||||
the serialization internally. The indentation level is propagated as
|
||||
additional parameter. In case of arrays and objects, the function is
|
||||
called recursively.
|
||||
|
||||
- strings and object keys are escaped using `escape_string()`
|
||||
- integer numbers are converted implicitly via `operator<<`
|
||||
- floating-point numbers are converted to a string using `"%g"` format
|
||||
|
||||
@param[in] val value to serialize
|
||||
@param[in] pretty_print whether the output shall be pretty-printed
|
||||
@param[in] depth the current recursive depth
|
||||
*/
|
||||
void dump(const BasicJsonType& val,
|
||||
const bool ensure_ascii,
|
||||
const unsigned int depth,
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
if (context.cbegin() != context.cend())
|
||||
{
|
||||
active_style = stylizer.get_new_style_or_active(*context.crbegin(), active_style);
|
||||
}
|
||||
|
||||
switch (val.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
dump_object(val, ensure_ascii, depth, active_style, context);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
dump_array(val, ensure_ascii, depth, active_style, context);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
dump_string(*val.m_value.string, ensure_ascii, active_style);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::boolean:
|
||||
{
|
||||
if (val.m_value.boolean)
|
||||
{
|
||||
o->write_characters("true", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_characters("false", 5);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
prim_serializer.dump_integer(*o, val.m_value.number_integer);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
prim_serializer.dump_integer(*o, val.m_value.number_unsigned);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
prim_serializer.dump_float(*o, val.m_value.number_float);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::discarded:
|
||||
{
|
||||
o->write_characters("<discarded>", 11);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
o->write_characters("null", 4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Iterator>
|
||||
void dump_object_key_value(
|
||||
Iterator i, bool ensure_ascii, unsigned int depth,
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
const auto new_indent = (depth + 1) * active_style->indent_step * active_style->multiline;
|
||||
const int newline_len = active_style->space_after_colon;
|
||||
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
o->write_character('\"');
|
||||
prim_serializer.dump_escaped(*o, i->first, ensure_ascii);
|
||||
o->write_characters("\": ", 2 + newline_len);
|
||||
dump(i->second, ensure_ascii, depth + 1, active_style, context.appended(i->first));
|
||||
}
|
||||
|
||||
void dump_object(const BasicJsonType& val,
|
||||
bool ensure_ascii,
|
||||
unsigned int depth,
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
if (val.m_value.object->empty())
|
||||
{
|
||||
o->write_characters("{}", 2);
|
||||
return;
|
||||
}
|
||||
else if (depth >= active_style->depth_limit)
|
||||
{
|
||||
o->write_characters("{...}", 5);
|
||||
return;
|
||||
}
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto old_indent = depth * active_style->indent_step * active_style->multiline;
|
||||
const auto new_indent = (depth + 1) * active_style->indent_step * active_style->multiline;
|
||||
if (JSON_UNLIKELY(indent_string.size() < new_indent))
|
||||
{
|
||||
indent_string.resize(indent_string.size() * 2, active_style->indent_char);
|
||||
}
|
||||
const int newline_len = (active_style->multiline ? 1 : 0);
|
||||
|
||||
o->write_characters("{\n", 1 + newline_len);
|
||||
|
||||
// first n-1 elements
|
||||
auto i = val.m_value.object->cbegin();
|
||||
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
||||
{
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style, context);
|
||||
o->write_characters(",\n", 1 + newline_len);
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(i != val.m_value.object->cend());
|
||||
assert(std::next(i) == val.m_value.object->cend());
|
||||
dump_object_key_value(i, ensure_ascii, depth, active_style, context);
|
||||
|
||||
o->write_characters("\n", newline_len);
|
||||
o->write_characters(indent_string.c_str(), old_indent);
|
||||
o->write_character('}');
|
||||
}
|
||||
|
||||
void dump_array(const BasicJsonType& val,
|
||||
bool ensure_ascii,
|
||||
unsigned int depth,
|
||||
const fancy_serializer_style* active_style,
|
||||
const json_pointer_t& context)
|
||||
{
|
||||
if (val.m_value.array->empty())
|
||||
{
|
||||
o->write_characters("[]", 2);
|
||||
return;
|
||||
}
|
||||
else if (depth >= active_style->depth_limit)
|
||||
{
|
||||
o->write_characters("[...]", 5);
|
||||
return;
|
||||
}
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto old_indent = depth * active_style->indent_step * active_style->multiline;;
|
||||
const auto new_indent = (depth + 1) * active_style->indent_step * active_style->multiline;;
|
||||
if (JSON_UNLIKELY(indent_string.size() < new_indent))
|
||||
{
|
||||
indent_string.resize(indent_string.size() * 2, active_style->indent_char);
|
||||
}
|
||||
const int newline_len = (active_style->multiline ? 1 : 0);
|
||||
|
||||
using pair = std::pair<const char*, int>;
|
||||
auto comma_string =
|
||||
active_style->multiline ? pair(",\n", 2) :
|
||||
active_style->space_after_comma ? pair(", ", 2) :
|
||||
pair(",", 1);
|
||||
|
||||
o->write_characters("[\n", 1 + newline_len);
|
||||
|
||||
// first n-1 elements
|
||||
for (auto i = val.m_value.array->cbegin();
|
||||
i != val.m_value.array->cend() - 1; ++i)
|
||||
{
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(*i, ensure_ascii, depth + 1, active_style,
|
||||
context.appended(i - val.m_value.array->cbegin()));
|
||||
o->write_characters(comma_string.first, comma_string.second);
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(not val.m_value.array->empty());
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(val.m_value.array->back(), ensure_ascii, depth + 1, active_style,
|
||||
context.appended(val.m_value.array->size()));
|
||||
|
||||
o->write_characters("\n", newline_len);
|
||||
o->write_characters(indent_string.c_str(), old_indent);
|
||||
o->write_character(']');
|
||||
}
|
||||
|
||||
void dump_string(const string_t& str, bool ensure_ascii,
|
||||
const fancy_serializer_style* active_style)
|
||||
{
|
||||
o->write_character('\"');
|
||||
if (active_style->strings_maximum_length == 0)
|
||||
{
|
||||
prim_serializer.dump_escaped(*o, str, ensure_ascii);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream ss;
|
||||
nlohmann::detail::output_adapter<char> o_string(ss);
|
||||
nlohmann::detail::output_adapter_t<char> oo_string = o_string;
|
||||
prim_serializer.dump_escaped(*oo_string, str, ensure_ascii);
|
||||
|
||||
std::string full_str = ss.str();
|
||||
if (full_str.size() <= active_style->strings_maximum_length)
|
||||
{
|
||||
o->write_characters(full_str.c_str(), full_str.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned start_len = [](unsigned int maxl)
|
||||
{
|
||||
if (maxl <= 3)
|
||||
{
|
||||
// There is only room for the ellipsis,
|
||||
// no characters from the string
|
||||
return 0u;
|
||||
}
|
||||
else if (maxl <= 5)
|
||||
{
|
||||
// With four allowed characters, we add in the
|
||||
// first from the string. With five, we add in
|
||||
// the *last* instead, so still just one at
|
||||
// the start.
|
||||
return 1u;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We subtract three for the ellipsis
|
||||
// and one for the last character.
|
||||
return maxl - 4;
|
||||
}
|
||||
}(active_style->strings_maximum_length);
|
||||
|
||||
const unsigned end_len =
|
||||
active_style->strings_maximum_length >= 5 ? 1 : 0;
|
||||
|
||||
const unsigned ellipsis_length =
|
||||
active_style->strings_maximum_length >= 3
|
||||
? 3
|
||||
: active_style->strings_maximum_length;
|
||||
|
||||
o->write_characters(full_str.c_str(), start_len);
|
||||
o->write_characters("...", ellipsis_length);
|
||||
o->write_characters(full_str.c_str() + str.size() - end_len, end_len);
|
||||
}
|
||||
}
|
||||
o->write_character('\"');
|
||||
}
|
||||
|
||||
private:
|
||||
/// the output of the fancy_serializer
|
||||
output_adapter_t<char> o = nullptr;
|
||||
|
||||
/// Used for serializing "base" objects. Strings are sort of
|
||||
/// counted in this, but not completely.
|
||||
primitive_serializer_t prim_serializer;
|
||||
|
||||
/// the indentation string
|
||||
string_t indent_string;
|
||||
|
||||
/// Output style
|
||||
const stylizer_t stylizer;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
std::ostream& fancy_dump(std::ostream& o, const BasicJsonType& j,
|
||||
basic_fancy_serializer_stylizer<BasicJsonType> const& stylizer)
|
||||
{
|
||||
// do the actual serialization
|
||||
detail::fancy_serializer<BasicJsonType> s(detail::output_adapter<char>(o), stylizer);
|
||||
s.dump(j, false);
|
||||
return o;
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
std::ostream& fancy_dump(std::ostream& o, const BasicJsonType& j,
|
||||
fancy_serializer_style style)
|
||||
{
|
||||
basic_fancy_serializer_stylizer<BasicJsonType> stylizer(style);
|
||||
return fancy_dump(o, j, stylizer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// #include <nlohmann/detail/json_ref.hpp>
|
||||
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
class json_ref
|
||||
{
|
||||
public:
|
||||
using value_type = BasicJsonType;
|
||||
|
||||
json_ref(value_type&& value)
|
||||
: owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
|
||||
{}
|
||||
|
||||
json_ref(const value_type& value)
|
||||
: value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
|
||||
{}
|
||||
|
||||
json_ref(std::initializer_list<json_ref> init)
|
||||
: owned_value(init), value_ref(&owned_value), is_rvalue(true)
|
||||
{}
|
||||
|
||||
template<class... Args>
|
||||
json_ref(Args&& ... args)
|
||||
: owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true)
|
||||
{}
|
||||
|
||||
// class should be movable only
|
||||
json_ref(json_ref&&) = default;
|
||||
json_ref(const json_ref&) = delete;
|
||||
json_ref& operator=(const json_ref&) = delete;
|
||||
|
||||
value_type moved_or_copied() const
|
||||
{
|
||||
if (is_rvalue)
|
||||
{
|
||||
return std::move(*value_ref);
|
||||
}
|
||||
return *value_ref;
|
||||
}
|
||||
|
||||
value_type const& operator*() const
|
||||
{
|
||||
return *static_cast<value_type const*>(value_ref);
|
||||
}
|
||||
|
||||
value_type const* operator->() const
|
||||
{
|
||||
return static_cast<value_type const*>(value_ref);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable value_type owned_value = nullptr;
|
||||
value_type* value_ref = nullptr;
|
||||
const bool is_rvalue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// #include <nlohmann/detail/json_pointer.hpp>
|
||||
|
||||
// #include <nlohmann/adl_serializer.hpp>
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user