🔨 templated output_adapter and used in class serializer

This commit is contained in:
Niels Lohmann 2017-04-14 19:49:05 +02:00
parent 2d2b98a50a
commit 717106eced
No known key found for this signature in database
GPG key ID: 7F3CEA63AE251B69
2 changed files with 154 additions and 96 deletions

View file

@ -2897,8 +2897,8 @@ class basic_json
*/ */
string_t dump(const int indent = -1) const string_t dump(const int indent = -1) const
{ {
std::stringstream ss; string_t result;
serializer s(ss); serializer s(output_adapter<char>::create(result));
if (indent >= 0) if (indent >= 0)
{ {
@ -2909,7 +2909,7 @@ class basic_json
s.dump(*this, false, 0); s.dump(*this, false, 0);
} }
return ss.str(); return result;
} }
/*! /*!
@ -6554,6 +6554,104 @@ class basic_json
/// @} /// @}
private:
/////////////////////
// output adapters //
/////////////////////
template<typename CharType>
class output_adapter
{
public:
virtual void write_character(CharType c) = 0;
virtual void write_characters(const CharType* s, size_t length) = 0;
virtual ~output_adapter() {}
static std::shared_ptr<output_adapter<CharType>> create(std::vector<CharType>& vec)
{
return std::shared_ptr<output_adapter>(new output_vector_adapter<CharType>(vec));
}
static std::shared_ptr<output_adapter<CharType>> create(std::ostream& s)
{
return std::shared_ptr<output_adapter>(new output_stream_adapter<CharType>(s));
}
static std::shared_ptr<output_adapter<CharType>> create(std::string& s)
{
return std::shared_ptr<output_adapter>(new output_string_adapter<CharType>(s));
}
};
template<typename CharType>
using output_adapter_t = std::shared_ptr<output_adapter<CharType>>;
template<typename CharType>
class output_vector_adapter : public output_adapter<CharType>
{
public:
output_vector_adapter(std::vector<CharType>& vec)
: v(vec)
{}
void write_character(CharType c) override
{
v.push_back(c);
}
void write_characters(const CharType* s, size_t length) override
{
std::copy(s, s + length, std::back_inserter(v));
}
private:
std::vector<CharType>& v;
};
template<typename CharType>
class output_stream_adapter : public output_adapter<CharType>
{
public:
output_stream_adapter(std::basic_ostream<CharType>& s)
: stream(s)
{}
void write_character(CharType c) override
{
stream.put(c);
}
void write_characters(const CharType* s, size_t length) override
{
stream.write(s, static_cast<std::streamsize>(length));
}
private:
std::basic_ostream<CharType>& stream;
};
template<typename CharType>
class output_string_adapter : public output_adapter<CharType>
{
public:
output_string_adapter(std::string& s)
: str(s)
{}
void write_character(CharType c) override
{
str.push_back(c);
}
void write_characters(const CharType* s, size_t length) override
{
str.append(s, length);
}
private:
std::basic_string<CharType>& str;
};
/////////////////// ///////////////////
// serialization // // serialization //
@ -6576,7 +6674,7 @@ class basic_json
/*! /*!
@param[in] s output stream to serialize to @param[in] s output stream to serialize to
*/ */
serializer(std::ostream& s) serializer(output_adapter_t<char> s)
: o(s), loc(std::localeconv()), : o(s), loc(std::localeconv()),
thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]),
decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]) decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0])
@ -6610,13 +6708,13 @@ class basic_json
{ {
if (val.m_value.object->empty()) if (val.m_value.object->empty())
{ {
o.write("{}", 2); o->write_characters("{}", 2);
return; return;
} }
if (pretty_print) if (pretty_print)
{ {
o.write("{\n", 2); o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls // variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step; const auto new_indent = current_indent + indent_step;
@ -6629,49 +6727,49 @@ class basic_json
auto i = val.m_value.object->cbegin(); auto i = val.m_value.object->cbegin();
for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{ {
o.write(indent_string.c_str(), static_cast<std::streamsize>(new_indent)); o->write_characters(indent_string.c_str(), new_indent);
o.put('\"'); o->write_character('\"');
dump_escaped(i->first); dump_escaped(i->first);
o.write("\": ", 3); o->write_characters("\": ", 3);
dump(i->second, true, indent_step, new_indent); dump(i->second, true, indent_step, new_indent);
o.write(",\n", 2); o->write_characters(",\n", 2);
} }
// last element // last element
assert(i != val.m_value.object->cend()); assert(i != val.m_value.object->cend());
o.write(indent_string.c_str(), static_cast<std::streamsize>(new_indent)); o->write_characters(indent_string.c_str(), new_indent);
o.put('\"'); o->write_character('\"');
dump_escaped(i->first); dump_escaped(i->first);
o.write("\": ", 3); o->write_characters("\": ", 3);
dump(i->second, true, indent_step, new_indent); dump(i->second, true, indent_step, new_indent);
o.put('\n'); o->write_character('\n');
o.write(indent_string.c_str(), static_cast<std::streamsize>(current_indent)); o->write_characters(indent_string.c_str(), current_indent);
o.put('}'); o->write_character('}');
} }
else else
{ {
o.put('{'); o->write_character('{');
// first n-1 elements // first n-1 elements
auto i = val.m_value.object->cbegin(); auto i = val.m_value.object->cbegin();
for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{ {
o.put('\"'); o->write_character('\"');
dump_escaped(i->first); dump_escaped(i->first);
o.write("\":", 2); o->write_characters("\":", 2);
dump(i->second, false, indent_step, current_indent); dump(i->second, false, indent_step, current_indent);
o.put(','); o->write_character(',');
} }
// last element // last element
assert(i != val.m_value.object->cend()); assert(i != val.m_value.object->cend());
o.put('\"'); o->write_character('\"');
dump_escaped(i->first); dump_escaped(i->first);
o.write("\":", 2); o->write_characters("\":", 2);
dump(i->second, false, indent_step, current_indent); dump(i->second, false, indent_step, current_indent);
o.put('}'); o->write_character('}');
} }
return; return;
@ -6681,13 +6779,13 @@ class basic_json
{ {
if (val.m_value.array->empty()) if (val.m_value.array->empty())
{ {
o.write("[]", 2); o->write_characters("[]", 2);
return; return;
} }
if (pretty_print) if (pretty_print)
{ {
o.write("[\n", 2); o->write_characters("[\n", 2);
// variable to hold indentation for recursive calls // variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step; const auto new_indent = current_indent + indent_step;
@ -6699,36 +6797,36 @@ class basic_json
// first n-1 elements // first n-1 elements
for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
{ {
o.write(indent_string.c_str(), static_cast<std::streamsize>(new_indent)); o->write_characters(indent_string.c_str(), new_indent);
dump(*i, true, indent_step, new_indent); dump(*i, true, indent_step, new_indent);
o.write(",\n", 2); o->write_characters(",\n", 2);
} }
// last element // last element
assert(not val.m_value.array->empty()); assert(not val.m_value.array->empty());
o.write(indent_string.c_str(), static_cast<std::streamsize>(new_indent)); o->write_characters(indent_string.c_str(), new_indent);
dump(val.m_value.array->back(), true, indent_step, new_indent); dump(val.m_value.array->back(), true, indent_step, new_indent);
o.put('\n'); o->write_character('\n');
o.write(indent_string.c_str(), static_cast<std::streamsize>(current_indent)); o->write_characters(indent_string.c_str(), current_indent);
o.put(']'); o->write_character(']');
} }
else else
{ {
o.put('['); o->write_character('[');
// first n-1 elements // first n-1 elements
for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
{ {
dump(*i, false, indent_step, current_indent); dump(*i, false, indent_step, current_indent);
o.put(','); o->write_character(',');
} }
// last element // last element
assert(not val.m_value.array->empty()); assert(not val.m_value.array->empty());
dump(val.m_value.array->back(), false, indent_step, current_indent); dump(val.m_value.array->back(), false, indent_step, current_indent);
o.put(']'); o->write_character(']');
} }
return; return;
@ -6736,9 +6834,9 @@ class basic_json
case value_t::string: case value_t::string:
{ {
o.put('\"'); o->write_character('\"');
dump_escaped(*val.m_value.string); dump_escaped(*val.m_value.string);
o.put('\"'); o->write_character('\"');
return; return;
} }
@ -6746,11 +6844,11 @@ class basic_json
{ {
if (val.m_value.boolean) if (val.m_value.boolean)
{ {
o.write("true", 4); o->write_characters("true", 4);
} }
else else
{ {
o.write("false", 5); o->write_characters("false", 5);
} }
return; return;
} }
@ -6775,13 +6873,13 @@ class basic_json
case value_t::discarded: case value_t::discarded:
{ {
o.write("<discarded>", 11); o->write_characters("<discarded>", 11);
return; return;
} }
case value_t::null: case value_t::null:
{ {
o.write("null", 4); o->write_characters("null", 4);
return; return;
} }
} }
@ -6872,7 +6970,7 @@ class basic_json
const auto space = extra_space(s); const auto space = extra_space(s);
if (space == 0) if (space == 0)
{ {
o.write(s.c_str(), static_cast<std::streamsize>(s.size())); o->write_characters(s.c_str(), s.size());
return; return;
} }
@ -6998,7 +7096,7 @@ class basic_json
} }
assert(pos == s.size() + space); assert(pos == s.size() + space);
o.write(result.c_str(), static_cast<std::streamsize>(result.size())); o->write_characters(result.c_str(), result.size());
} }
/*! /*!
@ -7018,7 +7116,7 @@ class basic_json
// special case for "0" // special case for "0"
if (x == 0) if (x == 0)
{ {
o.put('0'); o->write_character('0');
return; return;
} }
@ -7044,7 +7142,7 @@ class basic_json
} }
std::reverse(number_buffer.begin(), number_buffer.begin() + i); std::reverse(number_buffer.begin(), number_buffer.begin() + i);
o.write(number_buffer.data(), static_cast<std::streamsize>(i)); o->write_characters(number_buffer.data(), i);
} }
/*! /*!
@ -7060,7 +7158,7 @@ class basic_json
// NaN / inf // NaN / inf
if (not std::isfinite(x) or std::isnan(x)) if (not std::isfinite(x) or std::isnan(x))
{ {
o.write("null", 4); o->write_characters("null", 4);
return; return;
} }
@ -7069,11 +7167,11 @@ class basic_json
{ {
if (std::signbit(x)) if (std::signbit(x))
{ {
o.write("-0.0", 4); o->write_characters("-0.0", 4);
} }
else else
{ {
o.write("0.0", 3); o->write_characters("0.0", 3);
} }
return; return;
} }
@ -7114,7 +7212,7 @@ class basic_json
} }
} }
o.write(number_buffer.data(), static_cast<std::streamsize>(len)); o->write_characters(number_buffer.data(), static_cast<size_t>(len));
// determine if need to append ".0" // determine if need to append ".0"
const bool value_is_int_like = std::none_of(number_buffer.begin(), const bool value_is_int_like = std::none_of(number_buffer.begin(),
@ -7126,13 +7224,13 @@ class basic_json
if (value_is_int_like) if (value_is_int_like)
{ {
o.write(".0", 2); o->write_characters(".0", 2);
} }
} }
private: private:
/// the output of the serializer /// the output of the serializer
std::ostream& o; output_adapter_t<char> o = nullptr;
/// a (hopefully) large enough character buffer /// a (hopefully) large enough character buffer
std::array<char, 64> number_buffer{{}}; std::array<char, 64> number_buffer{{}};
@ -7181,7 +7279,7 @@ class basic_json
o.width(0); o.width(0);
// do the actual serialization // do the actual serialization
serializer s(o); serializer s(output_adapter<char>::create(o));
s.dump(j, pretty_print, static_cast<unsigned int>(indentation)); s.dump(j, pretty_print, static_cast<unsigned int>(indentation));
return o; return o;
} }
@ -8778,46 +8876,6 @@ class basic_json
const char* start; const char* start;
}; };
/////////////////////
// output adapters //
/////////////////////
class output_adapter
{
public:
virtual void write_character(uint8_t c) = 0;
virtual void write_characters(const uint8_t* s, size_t length) = 0;
virtual ~output_adapter() {}
static std::shared_ptr<output_adapter> create(std::vector<uint8_t>& vec)
{
return std::shared_ptr<output_adapter>(new output_vector_adapter(vec));
}
};
using output_adapter_t = std::shared_ptr<output_adapter>;
class output_vector_adapter : public output_adapter
{
public:
output_vector_adapter(std::vector<uint8_t>& vec)
: v(vec)
{}
void write_character(uint8_t c) override
{
v.push_back(c);
}
void write_characters(const uint8_t* s, size_t length) override
{
std::copy(s, s + length, std::back_inserter(v));
}
private:
std::vector<uint8_t>& v;
};
////////////////////////////////////////// //////////////////////////////////////////
// binary serialization/deserialization // // binary serialization/deserialization //
////////////////////////////////////////// //////////////////////////////////////////
@ -9872,7 +9930,7 @@ class basic_json
: is_little_endian(little_endianess()) : is_little_endian(little_endianess())
{} {}
explicit binary_writer(output_adapter_t adapter) explicit binary_writer(output_adapter_t<uint8_t> adapter)
: is_little_endian(little_endianess()), oa(adapter) : is_little_endian(little_endianess()), oa(adapter)
{} {}
@ -10379,7 +10437,7 @@ class basic_json
const bool is_little_endian = true; const bool is_little_endian = true;
/// the output /// the output
output_adapter_t oa = nullptr; output_adapter_t<uint8_t> oa = nullptr;
}; };
public: public:
@ -10468,7 +10526,7 @@ class basic_json
static std::vector<uint8_t> to_cbor(const basic_json& j) static std::vector<uint8_t> to_cbor(const basic_json& j)
{ {
std::vector<uint8_t> result; std::vector<uint8_t> result;
binary_writer bw(output_adapter::create(result)); binary_writer bw(output_adapter<uint8_t>::create(result));
bw.write_cbor(j); bw.write_cbor(j);
return result; return result;
} }
@ -10550,7 +10608,7 @@ class basic_json
static std::vector<uint8_t> to_msgpack(const basic_json& j) static std::vector<uint8_t> to_msgpack(const basic_json& j)
{ {
std::vector<uint8_t> result; std::vector<uint8_t> result;
binary_writer bw(output_adapter::create(result)); binary_writer bw(output_adapter<uint8_t>::create(result));
bw.write_msgpack(j); bw.write_msgpack(j);
return result; return result;
} }

View file

@ -53,7 +53,7 @@ TEST_CASE("convenience functions")
const char* escaped) const char* escaped)
{ {
std::stringstream ss; std::stringstream ss;
json::serializer s(ss); json::serializer s(json::output_adapter<char>::create(ss));
s.dump_escaped(original); s.dump_escaped(original);
CHECK(ss.str() == escaped); CHECK(ss.str() == escaped);
}; };